Compare commits

...

78 commits

Author SHA1 Message Date
Rikki Gibson 4df3296a0f
Update param-nullchecking proposal (#5397) 2021-11-10 15:17:49 -08:00
Mads Torgersen 9b15547365
Update LDM agenda 2021-11-10 07:15:16 -08:00
Charles Stoner 87b42f24ad
Update lambda-improvements.md to match language design decisions (#5388)
* Include anonymous methods for function types and conversions

* Remove Direct invocation section

* 'var' cannot be used as explicit return type

* No inferred type for discard

* Function type conversions are ignored for user-defined conversions

* Update better function member and better conversion from expression

* Warning converting method group to object

* Move open issues to dedicated section

* Function types are used in a few specific contexts

* Removed design meeting links (moved to championed issue)
2021-11-09 12:02:49 -08:00
Charles Stoner df1a9276ff
Fix relative links in params-span.md (#5389) 2021-11-08 11:01:59 -08:00
Bernd Baumanns 3369227509
Fix linq (#5390)
* missing curly brace
2021-11-08 10:57:20 -08:00
Charles Stoner 73077e88ca
Add proposal for params Span<T> (#5382) 2021-11-05 17:43:34 -07:00
AlekseyTs 28efe56136
Explicitly specify the order of evaluation for implicit Index/Range support (#5380)
This is a follow up on an LDM decision, see https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-11-01.md for more information.
2021-11-05 06:40:36 -07:00
CyrusNajmabadi 338b5908b6
Update LDM-2021-11-03.md 2021-11-03 17:22:19 -07:00
Fredric Silberberg e51570659f
Added LDM notes for November 3rd, 2021. 2021-11-03 17:12:26 -07:00
Nigel Bess 1f7a1340e1
Update LDM-2021-04-14.md (#5383)
Fixed typo
2021-11-03 10:12:12 -07:00
Fredric Silberberg b7f3a6653c
Corrected notes on Index and Range. 2021-11-02 09:44:28 -07:00
Fredric Silberberg d0176fe491
Correct ordering 2021-11-01 17:05:43 -07:00
Fredric Silberberg 3514aabe7c
Added LDM Notes for November 1st, 2021 2021-11-01 16:47:12 -07:00
Mads Torgersen 2246988f5d
Update LDM agenda 2021-11-01 10:28:22 -07:00
Charles Stoner 26ccd64e79
Update README.md 2021-10-31 00:04:48 -07:00
Fred Silberberg 43d9ed40c1
Update the interpolated string handler spec (#5365)
Updates the spec with the changes implemented in https://github.com/dotnet/roslyn/pull/57456, after discussion with LDM.
2021-10-29 21:02:04 -07:00
Alireza Habibi cb5871f620
Update list-patterns.md (#5364) 2021-10-29 13:44:15 -07:00
Charles Stoner 6a76c1cf68
Update README.md 2021-10-29 11:30:49 -07:00
Mads Torgersen 7bdf60f57a
Update LDM agenda 2021-10-28 17:54:44 -07:00
Charles Stoner 87137e67cc
Update README.md 2021-10-28 17:31:03 -07:00
Fredric Silberberg 81f2670ec5
Added LDM notes for September 27th, 2021 2021-10-27 17:50:30 -07:00
Mads Torgersen 2e10a1b56d
Update LDM agenda 2021-10-27 09:56:17 -07:00
Fred Silberberg 42b18c5d20
Fixup utf8 string proposal (#5355) 2021-10-27 09:50:16 -07:00
CyrusNajmabadi 0f09d0ffdd
Update README.md 2021-10-27 09:46:06 -07:00
Jared Parsons 5e25bf6e3a
Utf8 string literal proposal (#5349)
* Have to start somewhere

* Progress

* Progress

* Initial draft complete

* Apply suggestions from code review

Co-authored-by: Stephen Toub <stoub@microsoft.com>

* Suggestions

Co-authored-by: Stephen Toub <stoub@microsoft.com>
2021-10-27 08:49:24 -07:00
Mads Torgersen 8f13ded863
Update LDM agenda 2021-10-26 11:03:14 -07:00
Fredric Silberberg 2802e29f4c
Added LDM Notes for October 25th, 2021 2021-10-25 15:43:12 -07:00
Mads Torgersen ec01d26c47
Update LDM agenda 2021-10-22 14:50:59 -07:00
Mads Torgersen 2ba7e98c33
Add Nov and Dec dates to LDM agenda 2021-10-21 17:02:41 -07:00
Fredric Silberberg 9f54c9673f
Added LDM Notes for October 20th, 2021. 2021-10-20 16:27:56 -07:00
Julien Couvreur e8ddd37721
Remove value type restriction on nullable suppressions
A recent issue prompted me to double-check this: https://github.com/dotnet/roslyn/issues/57142
Original design decision: https://github.com/dotnet/roslyn/issues/29907
2021-10-19 16:57:48 -07:00
Mads Torgersen f20dd3076e
Update primary constructors proposal 2021-10-19 16:57:31 -07:00
Mads Torgersen 95ffad807f
Merge pull request #5306 from dotnet/madst
Create non-record-primary-constructors.md
2021-10-19 16:55:33 -07:00
Mads Torgersen ce40037cbf Update non-record-primary-constructors.md
React to @jaredpar comments about fields vs parameters
2021-10-19 16:47:39 -07:00
Mads Torgersen b6f2fb7598
Update proposals/non-record-primary-constructors.md
Co-authored-by: Jared Parsons <jaredpparsons@gmail.com>
2021-10-19 16:25:33 -07:00
Mads Torgersen 8265129d86 Create non-record-primary-constructors.md
Rewrite proposal from primary_constructors.md in light of the primary constructors already available on records.
2021-10-19 15:18:58 -07:00
Charles Stoner 90f617d0a7
Remove separate lambda-attributes.md proposal (#5301) 2021-10-18 18:11:39 -07:00
Mads Torgersen dd9c089e9e
Update LDM agenda 2021-10-15 13:33:54 -07:00
CyrusNajmabadi 762f5da1d2
Update semi-auto-properties.md 2021-10-15 12:53:13 -07:00
CyrusNajmabadi e4ffa88c9f
Update semi-auto-properties.md 2021-10-15 12:52:44 -07:00
CyrusNajmabadi b8fa5f0689
Update semi-auto-properties.md 2021-10-15 12:51:37 -07:00
CyrusNajmabadi d91984fe41
Update semi-auto-properties.md 2021-10-15 12:49:17 -07:00
CyrusNajmabadi cf102bcbb2
Create semi-auto-properties.md 2021-10-15 12:47:27 -07:00
CyrusNajmabadi a7aa8230e6
Update new-line-in-interpolation.md 2021-10-15 10:27:05 -07:00
CyrusNajmabadi f548a2fea8
Rename new-line-in-interpolation to new-line-in-interpolation.md 2021-10-14 14:05:11 -07:00
CyrusNajmabadi 0130349385
Create new-line-in-interpolation 2021-10-14 14:04:57 -07:00
Fredric Silberberg 7e2455955c
Added LDM notes for October 13th, 2021. 2021-10-13 15:34:01 -07:00
Mads Torgersen 6934787776
Update LDM agenda 2021-10-12 10:51:53 -07:00
Julien Couvreur 5675cc01cd
Update README.md 2021-10-08 16:55:36 -07:00
Charles Stoner a6737cff25
Several corrections to task-types.md (#5253) 2021-10-06 21:41:54 -07:00
Bill Wagner 1bbea06217
fix typos (#5250)
find / replace mistake, where I had // instead of /
2021-10-05 20:20:18 -04:00
AlekseyTs eedd69b8f9
Update "Unresolved questions" in static-abstracts-in-interfaces.md (#5249) 2021-10-05 14:49:13 -07:00
Bill Wagner f5daa3c480
Update links to spec (#5248)
Making the links to the spec relative links, so they'll resolve correctly both in this repo, and when published on docs.microsoft.com

(The latest change doesn't have the folder as part of it.)
2021-10-05 17:40:03 -04:00
Alireza Habibi 6e57bef953
Add pattern-variables.md (#4592) 2021-10-04 13:02:43 -07:00
Mads Torgersen 5a4d8a5903
Add LDM dates 2021-10-03 15:33:52 -07:00
Julien Couvreur a012a07b58
Update README.md 2021-10-01 14:42:07 -07:00
Fred Silberberg 0eb5952478
Fix the intro sections of the spec. (#5239) 2021-10-01 13:50:34 -07:00
Petr Onderka 41f962b156
Deleted confusing task lists from proposals (#5232) 2021-09-28 12:51:58 -07:00
Julien Couvreur 5c70a7acac
Update README.md 2021-09-28 10:56:55 -07:00
Julien Couvreur 40e8483671
Update README.md 2021-09-27 10:43:16 -07:00
CyrusNajmabadi 0ea5665946
Merge pull request #5174 from dotnet/CyrusNajmabadi-patch-1
Create using-alias-types.md
2021-09-24 17:50:00 -05:00
Charles Stoner d52e29f186
Lambda improvements: include 'Explicit return type inference' spec section (#5217) 2021-09-24 10:38:05 -07:00
Fred Silberberg c33a21734e
Fix example 2021-09-23 11:20:38 -07:00
Alireza Habibi 138765d6b0
Fix code example in notes (#5215) 2021-09-23 08:31:23 -07:00
Fredric Silberberg 38794f2c5b
Add missing notes link. 2021-09-22 17:39:46 -07:00
Fredric Silberberg 17d51b5875
Remove brackets 2021-09-22 17:37:42 -07:00
Fredric Silberberg 10f366592c
Added LDM notes fo rSeptember 22nd, 2021 2021-09-22 17:36:26 -07:00
Julien Couvreur 33e3bfe31f
Update README.md 2021-09-22 12:01:08 -07:00
Joseph Musser 088f20b6f9
Fix typos (#5210) 2021-09-21 09:53:30 -07:00
Charles Stoner fab06e432c
Correct formatting of generic type name in LDM notes (#5209) 2021-09-21 08:41:19 -07:00
Fredric Silberberg 4109c3d3de
Added LDM Notes for September 15th and 20th. 2021-09-20 18:21:14 -07:00
Mads Torgersen c1ccfd0d6a
Update LDM agenda 2021-09-19 09:54:00 -07:00
Charles Stoner 6f9ae4dccd
Update README.md 2021-09-17 15:52:13 -07:00
Mads Torgersen 5d5688cdac
Update LDM agenda 2021-09-17 15:45:48 -07:00
Mads Torgersen 794e7fa1d9
Update LDM agenda 2021-09-16 14:13:42 -07:00
CyrusNajmabadi 076e25b596
Update using-alias-types.md 2021-09-10 14:56:54 -07:00
CyrusNajmabadi ad321d2aac
Update using-alias-types.md 2021-09-10 13:21:23 -07:00
CyrusNajmabadi 611b638273
Create using-alias-types.md 2021-09-10 11:00:07 -07:00
37 changed files with 1922 additions and 280 deletions

View file

@ -57,7 +57,7 @@ the behavior here as it has also been a source of Roslyn-API consumer confusion
#### Conclusions
We will proceed with `field` as the keyword, and would like to have .NET 6 warning wave to nudge users to write code that has no change of being
We will proceed with `field` as the keyword, and would like to have .NET 6 warning wave to nudge users to write code that has no chance of being
ambiguous.
### Improved interpolated strings

View file

@ -20,7 +20,7 @@
- "Any proposals for a prioritization strategy?"
- "I'm not going to run a ranked choice algorithm in real time"
- "By small, I mean large"
- "Even Midori wasn't crazy enough to put ref and out into the type system" "Maybe that's went wrong"
- "Even Midori wasn't crazy enough to put ref and out into the type system" "Maybe that's what went wrong"
- "If you're using ref fields for something other than performance, come talk to me and we can find you help"
## Discussion

View file

@ -0,0 +1,73 @@
# C# Language Design Meeting for September 15th, 2021
## Agenda
* [Feedback from the C# standardization committee](#feedback-from-the-c-standardization-committee)
* [Permit pattern variables under disjunctive patterns](#permit-pattern-variables-under-disjunctive-patterns)
## Quote of the Day
- "We've so far decided not to decide"
## Discussion
### Feedback from the C# standardization committee
https://github.com/dotnet/csharpstandard/issues/366
For the first half of today's LDM, we had a guest from the C# standardization committee on to talk about the ongoing process by TC-49
to produce an update to the ECMA C# specification, the latest version of which covers C# 5. In the csharplang repository, we currently
have an initial conversion of an internal Microsoft C# specification of C# 3, which was updated to cover some aspects of C# 6. The ECMA
team has converted the C# 5 specification to markdown, and is currently working to integrate the changes from our C# 6 spec into that
version.
These dual versions of the specification can confuse users. The language reference on docs.microsoft.com comes from the csharplang
version of the spec, but the TC-49 version of this specification has had a number of bug fixes and is a better markdown conversion overall.
Because of this the csharplang version of the spec gets occasional pull requests to update various things, from English spelling and
grammar issues to actual spec bugs, but many times those issues have already been fixed in the TC-49 version of the specification. To
address this, we plan to remove the csharplang version of the specification when the last PRs for C# 6 are merged into the csharpstandard
repo, which should hopefully be soon.
Additionally, the standardization committee has draft PRs out for most C# 7 features, and is working on C# 8 specifications as well.
Currently, when they run into questions they've been emailing a few specific compiler team members, who hopefully can either answer their
questions or forward to the right team member. To facilitate better interactions, we've created an internal alias with the compiler team,
the language design team, and the TC-49 members currently working on the draft specifications. The specification changes for C# 8 are big,
particularly with nullable reference types, and will require close collaboration between these groups to make sure the spec actually reflects
the feature that was implemented by the compiler team.
Finally, we did some thinking about how to collaborate moving forward on new language features. We'd like to think about maintaining proposals
as branches on the TC-49 specification, to make sure that we are considering the real specification when we think about new language features
and ensuring that we can see other proposed changes to the specification as a whole when making new features, as opposed to having to remember
what not-yet-specified proposal from a previous language version modified a particular section of the specification while designing a new
change to that section.
### Permit pattern variables under disjunctive patterns
https://github.com/dotnet/csharplang/issues/4018
We took a first pass over this proposal in the second half of the LDM. At first brush, there are a couple of major points of contention:
1. Should we allow redeclaration, or should we have some form of `into` pattern that would allow the reuse of an existing variable in
a pattern?
2. Is variable declaration across multiple expressions ok, or should it only be permissible within a single pattern?
Point 1 arises from potential confusion around the double declaration: will users find the multiple declarations intuitive, or will they
wonder about "which" of the pattern variables future usages refer to? We've also had requests for an `into` pattern in the past, that would
allow a pattern to assign into an existing variable. We're concerned about a generalized version of this pattern because it could have
unpredictable effects, particularly when combined with `when` clauses, but a more specialized version that can only use variables declared
in the same pattern could be a usable version of this proposal. We also want to think about how this would interact with any potential
pattern matching over types themselves in the future, such as extracting the `T` from an `IEnumerable<T>`: if such a thing could be done,
it should be doable under an `or` as well, and we will want to have similar syntax forms for redeclaration as here. We also need consider
how an `into` pattern or other form of assignment syntax would interact if we ever want to permit non-constants to be used as a pattern
themselves.
For point 2, we're concerned about the potential large impact throughout the language. This would be very similar to permitting a
generalized `into` pattern, where `when` clauses can cause otherwise-matching patterns to not be executed and reassign existing variables.
Several members of the LDM feel that we should tackle just within a single expression first, and consider multiple expressions at a later
time with more examples of the types of code it would enable.
#### Conclusions
No conclusions today. We want to see the specification PR updated with more motivating samples, including samples from real code, before
we make any conclusions on these issues.

View file

@ -0,0 +1,168 @@
# C# Language Design Meeting for September 20th, 2021
## Agenda
1. [Lambda breaking changes](#lambda-breaking-changes)
2. [Newlines in non-verbatim interpolated strings](#newlines-in-non-verbatim-interpolated-strings)
3. [Object initializer event hookup](#object-initializer-event-hookup)
4. [Type alias improvements](#type-alias-improvements)
## Quote of the Day
- "You're kicking in an open door"
## Discussion
### Lambda breaking changes
https://github.com/dotnet/roslyn/pull/56341
In the ever continuing saga of new breaking changes introduced by giving method groups and lambda expressions natural types, we looked
at a few new breaking changes today to decide what, if any, workarounds we should adopt to try and fix them.
#### Breaking changes around overload resolution
https://github.com/dotnet/roslyn/issues/55691
https://github.com/dotnet/roslyn/issues/56167
https://github.com/dotnet/roslyn/issues/56319
https://github.com/dotnet/csharplang/discussions/5157
We have a number of reports from users who have been broken by changes in overload resolution, mostly because a set of overloads that
used to succeed in overload resolution are now ambiguous. A smaller group met to discuss a number of different potential solutions to
the issue. These options were:
1. Leave the breaking changes as-is.
2. Change “method type inference” and “best common type” to not infer from the natural type of a lambda expression or method group.
3. Change “better function member” to treat delegate types with identical signatures as equivalent, allowing tie-breaking rules to apply.
4. Change “better function member” to prefer overloads where method type inference did not infer type arguments from the natural types
of lambdas or method groups.
5. Change “better function member” to prefer parameter types that are delegate types other than those used for natural type. (Prefer `D`
over `Action`.)
6. Change “better function member” to prefer argument conversions other than “function type” conversions.
7. Change “better function member” to prefer parameter types `D` or `Expression<D>` over `Delegate` or `Expression`, where `D` is a delegate type.
Discussion further narrowed our focus to two combinations of the above options: 3+7 or 4+6. 3+7 results in a more aggressive break, while
4+6 is more compatible with previous versions of C#. Given the extent of some of the breaks we're seeing, we think the more compatible
approach is the better way to go, so we'll proceed with the PR linked at the start of this section.
##### Conclusion
Options 4+6 accepted.
#### Method groups converting to `Expression`
Another break testing has revealed looks like this:
```cs
var c = new C();
c.M(F); // C#9: E.M(); C#10: error CS0428: Cannot convert method group 'F' to 'Expression'.
static int F() => 0;
class C
{
public void M(Expression e) { Console.WriteLine("C.M"); }
}
static class E
{
public static void M(this object o, Func<int> a) { Console.WriteLine("E.M"); }
}
```
We think we would have a solution for this: split our "function type conversion" into two separate conversion types: a function type
conversion from lambda, and a function type conversion from method group. Only the former would have a conversion to Expression. This
would make it so that `M(Expression)` is not applicable if the user passed a method group, leaving only `M(object, Func<int>)`. This
could be a bit complex, but it should resolve the issue.
Unlike the previous examples, however, we don't have any reports of this issue. Given the number of reports of the previous breakages
we've received, and the lack of reports for this issue, we tentatively think that it's not worth fixing currently. If, after we ship
C# 10 for real, we received reports of this break, we know how to fix it and can change course at that time without making a breaking
change.
##### Conclusion
No changes will be made.
#### Lambdas in OHI
A final break we looked at today is:
```cs
using System;
B.F1(() => 1); // C#9: A.F1(); C#10: B.F1()
var b = new B();
b.F2(() => 2); // C#9: A.F2(); C#10: B.F2()
class A
{
public static void F1(Func<int> f) { }
public void F2(Func<int> f) { }
}
class B : A
{
public static void F1(Delegate d) { }
public void F2(Delegate d) { }
}
```
This is standard OHI behavior in C#, but because the derived overloads were previously not applicable, they were not included in the
`B.F1` or `b.F2` method groups, and only the methods from `A` would be applicable. Now that methods from the more derived type are
applicable, methods from the base type are filtered out by method group resolution.
We think this is both fine and actually desirable behavior. We don't have contravariant parameters in C#, but this is effectively
acting like such, which is a good thing. This change is also not customer-reported, but was instead discovered in testing. Given the
desirable behavior and lack of reports, we think no change is necessary.
##### Conclusion
No changes.
### Newlines in non-verbatim interpolated strings
https://github.com/dotnet/csharplang/issues/4935
We have a lot of compiler complexity around ensuring interpolated strings do not have a newline in them, and we don't see a real
reason to forbid newlines. We think the origin might have come from the number of different design flip-flops we made on interpolated
strings during their initial design.
#### Conclusion
Language change approved.
### Object initializer event hookup
https://github.com/dotnet/csharplang/issues/5176
LDM is not only interested in this change, we're also interested in generalized improvements that can be made in object initializers
and with expressions. Compound assignment is interesting, particularly in `with` expressions, and we would like to see what improvements
we could make not just for events, but for all types of properties and fields.
#### Conclusion
Approved. We want to explore even more enhancements in this space.
### Type alias improvements
https://github.com/dotnet/csharplang/issues/4284
Finally today, we looked at one of the open questions in this proposal: how should we handle nullable types in using aliases when the
alias is used in a `#nullable disable` location.
There are largely 2 ways to view using aliases:
1. Syntactic substitutions: the compiler is literally copy/pasting the thing in the alias into the target location. In this view, the
compiler should treat the syntax as occuring at the use point, and warn based on that.
2. Semantic substitutions: the using alias is effectively defining a new type. It's not a truly different type, but only the meaning
is substituted, not the actual syntax. If we ever want to consider a way to export using aliases, this will be a useful meaning to assume.
We also have some (possibly unintended) prior art here: `using MyList = System.Collections.Generic.List<string?>;` takes the second
approach today, acting like a semantic substitution.
The one thing we still want to consider in this space is top-level nullability. We're not sure about allowing a type alias to have
top-level nullability when it's an alias to a reference type. There is (very intentionally) no extra C# syntax for "not null reference
type" beyond the lack of a `?`, and the next ask if we were to allow aliases to be top-level nullable would be for such a syntax.
#### Conclusion
Overall, we like the semantic meaning. We still need to consider whether aliases should be allowed to have top-level nullability.

View file

@ -0,0 +1,136 @@
# C# Language Design Meeting for September 22nd, 2021
## Agenda
1. [Open questions in list patterns](#open-questions-in-list-patterns)
1. [Breaking change confirmation](#breaking-change-confirmation)
2. [Positional patterns on ITuple](#positional-patterns-on-ITuple)
3. [Slicing rules](#slicing-rules)
4. [Slice syntax recommendations](#slice-syntax-recommendations)
5. [Other list pattern features](#other-list-pattern-features)
2. [Nested members in `with` and object creation](#nested-members-in-with-and-object-creation)
3. [CallerIdentityAttribute](#calleridentityattribute)
4. [Attributes on `Main` for top level programs](#attributes-on-main-for-top-level-programs)
## Quote of the Day
- "That's not just our normal passive aggressive shtick, it's just actively aggressive."
## Discussion
### Open questions in list patterns
https://github.com/dotnet/csharplang/issues/3435
https://github.com/dotnet/csharplang/issues/5201
#### Breaking change confirmation
First, we looked at the breaking change for `Length`/`Count` on indexable and countable types. We're a bit concerned about making this
case a generalized error, even when a list pattern isn't being used: there are potential use cases for a `Length` that returns negative,
such as some object modeling a physics concept. We think we'd like to try and downgrade this to a warning instead of an error, for the
specific case where a negative length is tested and the location wasn't already subsumed. Some examples:
```cs
x is { Length: -1 } // Warning, not subsumbed by any previous case
x is { Length: >=0 } // Considered Exhaustive
x is { Length: <5 or -1 } // Error on the -1, subsumed by <5. No warning on the <5.
```
##### Conclusion
Try to make it a warning for non-subsumed cases.
#### Positional patterns on ITuple
Currently, we've implemented subsumption rules that allows list patterns and `ITuple` positional patterns to understand they refer to
the same alias. However, we think that this results in weird interactions with regular `ValueTuple` instances. `ValueTuple`s explicit
implement `ITuple`'s properties, so when pattern matching on one they would not be considered indexable or countable unless boxed into
`ITuple`. This would mean that, unless we were to make `ValueTuple` special in list patterns, there wouldn't be the same correspondence
as `ITuple` has. We also don't think that, in general, `Deconstruct` implies an order for an indexer: other than for `ITuple`, these
seem to be unrelated concepts.
##### Conclusion
There will be no correspondence between positional patterns and list patterns, even for ITuple. If someone comes along with a killer
scenario for ITuple later, we can readd this.
#### Slicing rules
These rules, as stated, matches our previous discussions on the topic.
##### Conclusion
No changes.
#### Slice syntax recommendations
We have two general choices for the slice pattern:
```cs
e is [ first, .. var rest ] // Space
e is [ first, ..var rest ] // No space
```
We feel that the code reads oddly without the leading space because the pattern being applied itself has a space. It introduces an odd
feeling of `(..var) rest`, even though the code is really `.. (var rest)`.
##### Conclusion
We're in favor of having a space.
#### Other list pattern features
For `IEnumerable` patterns, we'd like to introduce them at the same time as the rest of the list pattern feature, even if in a more
limited form, to ensure we avoid any potential breaking changes. They may go to preview at different times, and we may need to section
off some of the pattern features to ensure we can ship, but we'd like to at least have an initial version. We also do want to make
sure that we're generating as good code for this as possible, which may overlap some with params Span work to see whether we can
stackalloc.
For indexer patterns, we are interested, but they don't need to be this version of C#. We do think that we'll want them in the near
future though: we have lists, we should also be able to have dictionaries.
For length patterns, we're not seeing the need. We explored them previously and the exploration seems to have concluded that they might
be needed for IEnumerable, but if so we can revisit as we look at IEnumerable again.
### Nested members in `with` and object creation
https://github.com/dotnet/csharplang/issues/4587
We like the concept of reducing the boilerplate for nested with patterns. However, we're not a fan of the strawman syntax. Multiple members
of the LDM misinterpreted it as modifying the nested instance, rather than doing a `with` on the nested property. It would also fall over
if multiple properties on a nested instance need to be set. We have a couple of initial ideas:
```cs
methodCallExpression with { Method with { Name = "MethodName", BaseType = "C" } }
methodCallExpression with { Method = { Name = "MethodName", BaseType = "C" } } // more similar to nested object initializer?
```
#### Conclusion
Into the working set, with some syntax modifications.
### CallerIdentityAttribute
https://github.com/dotnet/csharplang/issues/4984
Mostly a convenience feature for ASP.NET which will help them structure code in a way more conducive to AOT. It seems in line with our
existing caller info attributes, so we'll work with the BCL and ASP.NET teams to prioritize the issue.
#### Conclusion
Into the working set.
### Attributes on `Main` for top level programs
https://github.com/dotnet/csharplang/issues/5045
We do think there needs to be some design work: the feature proposes allowing the `main` modifier in any file. We need to decide whether
that's a use case we want to support, and if we do, whether we should allow the target with regular entry points or only with top level
statements. If we allowed it with any entry point, we think there are some decent engineering challenges with the way the language is
currently structured that would make it more complex than first blush might indicate. Some form of this feature seems reasonable though,
so we'll work with the BCL to determine the right scope of the feature.
#### Conclusion
Into the working set.

View file

@ -0,0 +1,97 @@
# C# Language Design Meeting for October 13th, 2021
## Agenda
1. [Revisiting DoesNotReturn](#revisiting-doesnotreturn)
2. [Warning on lowercase type names](#warning-on-lowercase-type-names)
3. [Length pattern backcompat](#length-pattern-backcompat)
## Quote of the Day
- "You're opinionated against opinionated features" "I see the irony, but..."
## Discussion
### Revisiting DoesNotReturn
https://github.com/dotnet/csharplang/issues/5231
We wanted to revisit this design decision after a few years and some reports of customer confusion around this attribute.
While it's not an overwhelming amount of confusion, it's non-zero. When last this attribute was
[discussed](../2019/LDM-2019-07-10.md#doesnotreturn), we decided that we couldn't use it to broadly enforce the concept of
a method that does not return: instead, it could only influence nullable analysis. Much like the rest of the features
nullable analysis added, it cannot make strong guarantees. Changing this behavior now wouldn't really solve any of the
issues with it, as code exists today that is attributed with `DoesNotReturn` that cannot be statically-proven to never
return.
Instead, we think that https://github.com/dotnet/csharplang/issues/538 would be needed to make progress here: once we have
a `never` type, these guarantees can be statically proven, the runtime can abort if an instance of `never` is ever created,
and we can introduce a warning wave at that point to suggest that methods attributed with `DoesNotReturn` should have a
return type of `never`. This would prevent us from introducing another loose generalized analysis that can't be statically
relied upon before later adding a more strict version that can be relied upon.
#### Conclusion
No changes.
### Warning on lowercase type names
https://github.com/dotnet/roslyn/issues/56653
This is an issue that we've been kicking around for a long time to try and protect language design space, and we've recently
started to make more breaks in this space. For example, C# 9 introduced `record` as a type specifier, and made it illegal
to declare a type named `record` without escaping it. We've heard no complaints about this change, and this has given us
more confidence to go ahead with a warning in this area.
And important point is that this change is _not_ motivated by style. C# as a language tries hard not to be opinionated on
coding styles; we have defaults for formatting that are part of VS and `dotnet new editorconfig`, but these are highly
customizable and we try not to punish users for taking advantage of that customizability. Instead, this warning is about
trying to ensure that C# has design space to work with that doesn't break users who upgrade to new C# versions. This goal
means we're trying to help 2 sets of customers:
1. People who author types with lowercase names. These authors should be informed that they could be causing issues for their
users by using a lowercase name.
2. People who use types with lowercase names. These users should be informed that, if a future version of C# adds the type
they're using as a keyword, their code could change meaning or no longer compile.
Some other points of consideration we brought up:
* Should we do this for just public types?
* Answer: we are trying to protect users from both themselves and others. This means we want to warn everywhere.
* Should we have codefixers to prepend `@` in front of identifiers?
* Ultimately this will be up to the IDE team, but we think that each set of customers above should have a different answer
here. For set 1, we don't want to encourage this type of naming, even when escaped, so we would not want to see a fixer on
type definitions. However, for set 2, they're just using a type someone else defined. We don't want to unreasonably punish
them, so a fixer to prepend `@` for these users seems appropriate.
#### Conclusion
The compiler will issue a diagnostic for types named all lowercase letters. We may simplify that to be just types named all
ASCII lowercase characters, to avoid worrying about lowercase characters in other encodings and because we don't believe C#
will be interested in adding non-ASCII keywords.
### Length pattern backcompat
https://github.com/dotnet/csharplang/issues/5226
There are user tradeoffs here. In our previous discussion, we expressed a desire to have a similar ability to nullable, whereby
a missing negative Length test would not count against exhaustive matching, but if one was present it would then cause negative
values to need to be fully matched against. However, describing these rules and implementing them is quite complex, to the point
that we are concerned both about the code complexity and the explanation of the rules to users. We therefore think that the
simpler implementation, where we just consider the domain of `Count`/`Length` for indexable and countable types to be non-negative
integers, to be the better approach.
While we were discussing this, we also brought up the inherent flaw of structural typing, where types that do not satisfy the contract
of indexable and countable but still have the correct shape end up having incorrect behavior. For example, the BCL recently approved
a `Vector2<T>` type. That type has an indexer, and it has a property called `Length` that returns an integer. This `Length` property
is not the length of the Vector: that's always 2. Instead, this is the euclidean length of the vector, ie the distance from (0, 0).
Our knowledge of `Length` and combining subsumption with list patterns will behave incorrectly here. However, we note that this type
is _already_ broken with structural typing in C# today. For example, `vector[^1]` would work on the type, but it wouldn't have the
expected behavior. Instead of trying to solve something here, we instead think we should see what other uses customers have for opting
out of structural typing, and started a discussion about that [here](https://github.com/dotnet/csharplang/discussions/5278).
#### Conclusion
We will accept the existing breaking change on subsumption for indexable and countable types, and treat Length/Count on types that are
both indexable and countable as if they can _only_ have non-negative values. We would like to get this change in preview sooner
rather than later so that we can receive feedback on the change.

View file

@ -0,0 +1,93 @@
# C# Language Design Meeting for October 20th, 2021
## Agenda
1. [Open questions in list patterns](#open-questions-in-list-patterns)
1. [Types that define both Length and Count](#types-that-define-both-length-and-count)
2. [Slices that return null](#slices-that-return-null)
2. [Primary constructors](#primary-constructors)
## Quote of the Day
- "No fingerprints today, I always struggle with it after a bank robbery, when I file them down"
## Discussion
### Open questions in list patterns
https://github.com/dotnet/csharplang/issues/5137
We had 3 open questions from tests on list patterns. Questions 1 and 2 both relate to types with both `Length` and `Count` properties, so our decision
applies to both.
#### Types that define both Length and Count
Our previous definition of a type that can be matched by a list pattern is that the type must be both indexable and countable. Because countable could be
one of two properties, `Length` or `Count`, we need to decide what do when a type has both. Our options are:
1. Treat them as equivalent and assume they return the same value. This will affect subsumption.
2. Treat the non-recognized one (`Count` in the case where both `Length` and `Count` are defined) as not being special at all.
Conceptually, we think it would be odd to assume that `Count` is equal to `Length`, even though we'll never use it for any form of slicing or length checking.
While it might be reasonable to assume they return the same value, we don't think it's an important scenario. We'll have nearly a year of preview on this
feature, so we have plenty of time to react to feedback if dogfooding shows that it's an important scenario.
##### Conclusion
We will not treat `Length` and `Count` as being connected when both are defined.
#### Slices that return null
We believe that well-defined slice methods should never return `null` for an in-bounds range, and we'll never generate a call to such a slice method with
an out-of-bounds range. However, we also don't think there's a customer for this scenario: a search of GitHub showed no `Slice` methods that return an
annotated value, and treating a slice method differently than its annotation would require both implementation effort and customer education. Given that
there are no known cases this affects, we don't think it's worth it. Similarly to the first question, if such scenarios are identified during preview, we
can correct appropriately.
##### Conclusion
If a `Slice` method is annotated, we will treat it as potentially returning `null` for the purposes of subsumption and exhaustiveness.
### Primary constructors
https://github.com/dotnet/csharplang/issues/2691
Now that records have been out for a year and we've expanded them to struct and reference types, we think it's time to start looking at primary constructors
again. We updated the proposal with the new syntax forms from our records work, removing or modifying components where they make sense. Unlike for records,
primary constructors aren't about defining the members that make up a grouping of data: instead, they're purely for defining the inputs to a class. So things
like deconstruction, equality, and public properties aren't automatically generated from the parameters. Instead, they become fields, that are then eliminted if
the field is never used outside of a field initializer.
We think there are a couple of open discussion topics:
1. Should the fields be readonly or not?
2. How important are primary constructor bodies for a generalized feature?
For question 1, we think that, for better or for worse, C# is a mutable-by-default language. We were able to change the defaults for `record` types because
mutability in a value-based reference type is actively harmful, but we have to consider a number of naming and mutability issues we were able to skirt around
in record types. Field naming conventions, for example, are heavily split in C#, with some users using a leading `_`, and some preferring to just use pascalCase
with no leading modifiers. We think `readonly` is similar: if users would like to modify the defaults of C#, they can define their own field and do so. If this
proves to be the wrong default we can make a change before it ships for real, or potentially invest in https://github.com/dotnet/csharplang/issues/188 to allow
putting modifiers on parameters.
For question 2, we think that it will be important, but that once https://github.com/dotnet/csharplang/issues/2145 is in the language, a good portion of
initialization code will be expressable in just a single initialization expression. It will miss some more complex scenarios, but we think just primary constructors
are a good first step.
In general, we also want to make sure we're defining the difference between primary constructors and required properties well. With the potential for multiple new
initialization features to ship in a single language version, we want to make sure they complement each other well. We think required properties work well for
public contracts: DTOs, POCOs, and other similar, nominal data structures that will expose these properties externally. Primary constructors, on the other hand,
are for other types, that do not define public surface area based on their parameters. Instead, they simply define input parameters, and can assign them to private
fields or otherwise manipulate them as they choose.
Some concrete feedback on the proposal:
* The exception for calling `this` for copy constructors feels premature, as they don't mean anything to any type except record types. If we generalize `with` in
the future, then the exception will make sense, and we can add it then.
* We should disallow methods with the same name as primary constructor parameters. This is confusing and while it could potentially be understandable code if the
parameter was never captured, could then have spooky action at a distance if the parameter was accidentally captured when a user forgot to write the `()` of the
method name.
#### Conclusion
These words are accepted.

View file

@ -0,0 +1,104 @@
# C# Language Design Meeting for October 25th, 2021
## Agenda
1. [Required members](#required-members)
2. [Delegate type argument improvements](#delegate-type-argument-improvements)
## Quote of the Day
- "It's officially late, not casually late"
## Discussion
### Required members
https://github.com/dotnet/csharplang/issues/3630
It's been nearly a year since we last looked at required members, so we started today by recaping the proposal as it currently
[exists](https://github.com/dotnet/csharplang/blob/ec01d26c47d8d3e3e10b1dd0ade96dae6f99934a/proposals/required-members.md). We also
took a look at the feedback we received when this was last shown to our design review team (mainly internal partners and former
language design members who aren't involved the day-to-day design of the language anymore). The feedback we received at that point
was that the proposal, as it currently stands, is too complex. We have a lot of syntax for enabling a set of contract modifications:
1. Users can say `init(Prop)` to signal that the constructor requires all things the type does, except the members in the `init` clause.
2. Users can say `init required` to signal that the constructor does not require any of the members the type does, and the compiler
will ensure that the user correctly initializes all members in that constructor.
3. Users can say `init required!` to signal the same 2, except that the compiler will not enforce that the user correctly initializes
all members in that constructor.
This syntax was quite specialized, as we intended it to be extensible enough that future work around factories would be able to take
advantage of the same syntax. However, upon coming back to the syntax, we have several people that changed their opinions on the form;
some of the people that didn't originally feel like the syntax "clicked" now like it, and others that originally liked it now think that
it doesn't work well. We took a look at the concrete cases that will want to use the above modifications:
* Copy constructors will want to opt out of the entire contract, as their job is to initialize all the fields to the original instance's
values.
* Types might have a grow-up story, where they may originally ship with just some number of required properties, but later may want to add
a convenience constructor that sets a few common properties. We think that this case, while not unreasonable, is not the common case for
this feature.
The first use case wants at least contract modification 3, and possibly 2 as well. It does not need 1. The second case needs contract
modification 1 to be expressible in C#. Given this, we think that there's a gradual approach we can take to introducing contract
modifications to the language. A potential first approach is just introducing modification 3, without any constructor body validation.
A later version of the language can, as part of a warning wave, turn on body validation, and then later allow individual member exceptions
as in modification 1. We are a bit split on this decision, however. We think a prototype to play around with that implements this initial
decision should help inform the initial feature set we want to ship for this feature. One thing we definitely don't want to do is ship
without constructor body validation and without feeling like we can make that the default state later via a warning wave, as that would
leave us in a bad position where validation is available, but off by default.
We also looked at different syntax forms, namely attributes. We'd considered attributes previously in the design, but steered away from
them to see what we could do with language-integrated syntax. We think we've gone as far down that road as we can, but our final design
still doesn't feel quite right. Given that, we think that having an attribute to express the contract modifications will serve us well.
It can be named more verbosely, should still be able to apply to factory methods, and required no new syntax for users to learn.
Finally, we also considered a couple of other questions:
* Should we have a new accessor, `req`, that is used for a required property instead of `init`?
* We think that this use case is too small, cuts out required setters, and cuts out the ability to mark fields as required.
* Should we use a warning or error on construction? Traditionally, "suppressions" correspond to warnings, and the contract modifications
can be viewed as suppressing the need to initialize things.
* They're not really suppressions though. It's changing the semantics of the code, so we think it's fine (and safer) for us to use
errors on construction, not warnings.
* We haven't considered how required members will interact with `default(StructType)`. Some thought needs to be put into that.
#### Conclusion
We will start working on an implementation of the simplified version of this proposal that uses attributes for contract modifications,
not specific syntax, and only has contract modification 3. Feedback will then inform whether we need 2 or 1 before we ship.
### Delegate type argument improvements
https://github.com/dotnet/csharplang/issues/5321
A couple of weeks ago, Microsoft held our annual internal hackathon, and one of the ideas that we worked on for the it this year was
relaxing type argument restrictions, specifically for delegate types. This would allow delegate types such as `Action<void>` or
`Action<in int, ref readonly Span<char>>`, where these types are restricted from use in delegate types today. In general, the LDM has
a lot of interest in this space, but there is some concern that this proposal doesn't go far enough in relaxing restrictions, and might
run into issues if we try to generalize it more fully. A very common pattern in generic code in C# is to further abstract over parameter
types: for example, a method might take a `Func<T, bool>` and a `T`, and pass the `T` parameter to the `Func<T, bool>` invocation. This
proposal falls apart for such methods, which is a very early cliff to fall over. If we can instead generalize this work, such that these
restricted types are allowed in more places, then we'd be in a much better position. Even if we don't ship the whole thing at once, we'd
really like to make sure we're not painting ourselves into a hole with a proposal like this.
As a part of this, we also want to explore a revamp to the .NET delegate story. Today, `Action` and `Func` are the de-facto delegate types
in C#: they're used throughout the BCL and lambdas/method groups use them where possible for their natural type. However, there are limits
to this, as custom delegate types still have some advantages:
* Ref kinds/restricted type arguments. This is solved by the current proposal as it stands.
* Parameter names. We don't think this would be very difficult to solve, as we have prior art with tuple names.
* Attributes. This is much more difficult to accomplish, to avoid needing to synthesize custom delegate types we'd need to have some way of
putting attributes on a type argument. The runtime does not support this today, and it won't be as simple to add as the type restriction
removals were.
We've been moving towards structural typing of delegates ever since `Action` and `Func` were introduced, and we think it might be a better
approach to the problem to start from fully structural delegate types, and work backwards to see how we could make that work. It's very
likely that we'd end up doing some of the same work generalizing on `Action` and `Func`, but there's some additional interesting wrinkles
around `Action` and `Func<void>`. We might be able to unify these two, but we then want to be able to do the same for other types. How
would we make it work for `Task` and `Task<void>`, for example? Is there some attribute we could add to the runtime that would allow it
to treat these types as identical, forwarding from one to the other? And how would that flow through the C# type system?
#### Conclusions
We don't have any conclusions today. We think that much more exploration of this space is needed before moving forward with any specific
proposal. We want to look into generalizing the generic restriction removals, and into generalized structural typing for delegates.

View file

@ -0,0 +1,79 @@
# C# Language Design Meeting for October 27th, 2021
## Agenda
1. [UTF-8 String Literals](#utf-8-string-literals)
2. [Readonly modifiers for primary constructors](#readonly-modifiers-for-primary-constructors)
## Quote of the Day
- "I fought for that when I was young and idealistic"
## Discussion
### UTF-8 String Literals
https://github.com/dotnet/csharplang/issues/184
UTF-8 strings are an extremely important part of modern day programming, particularly for the web. We've previously talked about including special support for literals
based on UTF-8 in C#, as currently all our literals are UTF-16. It is possible to manually obtain byte arrays of UTF-8 bytes, but the process for doing so is either:
1. Error-prone, if you're hand-encoding a byte array.
2. Cumbersome and slightly inefficient, if you're creating static data to be later used.
3. Very inefficient, if you're converting the bytes every invocation.
We'd like to address these issues, doing the minimum possible work to make current scenarios more palatable without blocking our future ability to innovate in this space.
Currently, the runtime does not have a real `Utf8String` type, and instead uses `byte[]`, `Span<byte>`, or `ReadOnlySpan<byte>` as the de-facto interchange type for UTF-8
data. Many members of the LDM are concerned that, if we bless these types with conversions in the language, we will limit our future ability to react to the addition of
such a type into the runtime itself. In particular, if `var myStr = "Hello world"u8;` meant any of those three types, we lose out on the ability to make it mean `Utf8String`
in a future where that is added. This issue is further compounded because we don't know that such a type _will_ be added in the future: ongoing discussions are still being
had over whether a dedicated type will be added, or if the runtime could just have a flag to opt into having all `System.String` instances just become UTF-8 under the hood.
The u8 suffix had mixed reception with LDM. On the one hand, turning strings into one of the interchange types (either implicitly via target-typing, or only via explicit
cast) is convenient from a programming perspective. It also doesn't create a blessed syntax form that runs into the problems of the previous paragraph. On the other hand,
there is no direct indication _how_ the strings are being converted to bytes, which the suffix is useful for. Are the bytes just a direct representation of the UTF-16 data,
or an encoding the string in UTF-8 bytes?
Another question is whether a language-specific conversion is the correct approach here, or if we could create a user-defined conversion from `string` to `byte[]`. This
would allow non-constants to be converted as well, and the language/compiler could make the conversion special such that it could be optimized to occur at compile-time
when constant data is involved. It does suffer from the same problems of what byte encoding is being used, however, and we have existing solutions for converting non-constant
strings to UTF-8.
We also discussed a few other questions:
* Should we have a u8 character literal?
* People cast `char`s to `byte`s in a number of places today, this would need a suffix.
* It would also still need to return a sequence of bytes, rather than a single byte, so what advantage would it bring over a single-character string literal?
* Should the byte sequence have a null terminator?
* Interop scenarios will want a null terminator. However, they can add one by including a `\0` at the end of the string, and an analyzer can catch these cases.
* On the other hand, most managed code scenarios would break if a terminator was included.
* How specific should we make the language around the compile-time encoding of a given string literal?
* We'd like to make it relatively loose, such that we can say "the compiler is free to implement the most efficient and allocation-free form possible."
#### Conclusion
We don't feel confident enough to make any final calls today, but we'd like to make a prototype with target-typed literals and get some usage feedback. We'll implement
that behind a feature flag, and give the compiler to our BCL and ASP.NET partners so they can give us their thoughts from real API usage.
### Readonly modifiers for primary constructors
https://github.com/dotnet/csharplang/discussions/5314
https://github.com/dotnet/csharplang/issues/188
We wanted to revisit this question after we received a large amount of feedback from the community after our [last meeting](./LDM-2021-10-20.md#primary-constructors) on
primary constructors. The feedback was notable in particular because of the volume, as our discussion issues don't normally generate as much discussion from as many separate
community members as this one did. The feedback generally tended in one direction: mutability is an ok default, especially given the history of C#. However, they would like
a succinct way to make the generated fields `readonly`, without having to fall back to manual field declaration and assignment. To address this, we took a look at another
longstanding C# proposal, allowing `readonly` on parameters and locals. Several LDM members are concerned about this as a general feature, mainly because of the "attractive
nuisance" issue. `readonly` is often a sensible default for locals and (in particular) parameters, but in practice LDM members do not find accidental local/parameter mutation
to be a real source of bugs. Meanwhile, if we introduce this feature in general, people will start to use it, and require usage where possible in their codebases. It can add
clarity of intent for reading later, but many members are not convinced it meets the bar with C# 20 years old.
However, scoping the feature down to just primary constructor parameters was slightly less controversial. Most members of the LDM are not opposed to the concept in general,
but we remain conflicted as to whether we need to have such a feature immediately. While we did get a good deal of immediate feedback, some LDM members are unsure whether
this feedback will persist after users actually get their hands on the feature and give it a try. We could also take primary constructors in a more radically different direction,
where we'd adopt a more F#-like approach and consider these parameters captures, not fields.
#### Conclusion
We did not come to any conclusions today. We will revisit soon to talk more about this area.

View file

@ -0,0 +1,73 @@
# C# Language Design Meeting for November 1st, 2021
## Agenda
1. [Order of evaluation for Index and Range](#order-of-evaluation-for-index-and-range)
2. [Collection literals](#collection-literals)
## Quote of the Day
- "So 'The customer wants it therefore it's wrong' is your new motto?"
## Discussion
### Order of evaluation for Index and Range
https://github.com/dotnet/roslyn/issues/57349
We looked at an inconsistency in the order of evaluation between `Index` and `Range` operations, when those are applied to a type that does not have
native methods that accept `Index` or `Range` types. The `Index` portion of the spec is fairly straightforward and we were easily able to determine
the compiler had a bug with the lowering it performed. However, the specification is unclear about the `Range` translation: it does not clearly
communicate the order of evaluation for the `receiver`, `Length`, and `expr` expressions that are cached. Our current behavior is inconsistent with
the `Index` behavior, and likely confusing to users. After a bit of discussion, we unanimously agreed to standardize the order of evaluation to be
consistent with the written original order in the source, followed by compiler-needed components if necessary. For indexers, this is `receiver`, then
`expr`, then `Length` if needed.
#### Conclusion
Codify the expected behavior as `receiver` then `expr` then `Length`.
### Collection literals
https://github.com/dotnet/csharplang/issues/5354
We took a first look at a proposal for a "collection literal" syntax in C#, doing a general overview of the proposed syntax form and gathering general
feedback on the proposal. We hope to approach this proposal as a "one syntax to rule them all" form, that can achieve correspondence with the list
pattern syntax form, support new types that collection initializers can't today (such as `ImmutableArray<T>`), and serve as a zero or even negative
cost abstraction around list creation that is as good or better than hand-written user code.
There's some unfortunate bad interaction with existing collection initializers, however. They'll still exist, and they will be advantageous in some
cases. Since the user is explicitly calling `new Type` on them, they have an obvious natural type, while collection literals will need some form of
target-type for cases where there is no obvious natural type or if the user is trying to select between ambiguous overloads. They also probably wouldn't
support this new splat operator, which also makes them worse in that case; even if we extended support for it, it's very likely that the new form would
lower to a more efficient form with the pre-length calculation that it able to support.
We're unsure about some of the various axes of flexibility in this space. Some of them that we talked about are:
* Existing collection initializers require the type to implement `IEnumerable` to be considered collection-like. Do we want to keep this restriction for
the new form? It would lead to an odd non-correspondence with list patterns, since they are pattern-based and not interface-based. The proposal actually
started more restrictive, but loosened to the current form after considering things like `HashSet<int> h = [1, 2, 3];` and deciding that was perfectly
fine.
* How specific do we want to make the lowering for various forms? Since it's a goal of the feature to be as optimal as possible here, if we prescribe
too specific of lowering forms then we potentially make it difficult to optimize the implementation. We know from experience, though, that while we'll
have a small window of opportunity just after ship to make changes to the way code is emitted, making changes years on will be dangerous and will likely
have impacts on some user's code.
* We think that a natural type for this syntax will have a lot of benefits, such as allowing it to be used for interfaces (`IEnumerable<int> ie = [1, 2];`)
and it will be in line with the recent work we did around lambda expressions. However, unlike lambdas, we don't have a single list type in .NET that is
unambiguously the natural type it should be. We'll need to dig into the pros and cons of the space and decide on how we want to approach this.
We also talked briefly about possible extensions of this space. One obvious one is dictionary literals. We think we can move ahead with collection
literals for now, as long as we keep both dictionary literals and dictionary patterns, to be sure we're holding ourselves to the correspondence principle.
Another area we should investigate is list comprehensions. We're not sure whether we want comprehensions, but we should at least look at the space and
make sure we're comfortable that we'd never consider them or leave ourselves the space to be able to consider them in the future.
Finally, we want to look at other contemporary languages and see what they do for this space. For example, F# has dedicated syntax forms for each of it's
main list/sequence types. Unlike C#, they have largely unified on a very few specific collection types. This allows them to have dedicated syntax for each
one, but this approach isn't likely to work well in C# because we have a much wider variety of commonly-used collections, tuned for performance across a
variety of situtations. Kotlin takes a different approach of having well-known methods to construct different collections, such as `listOf` or `arrayOf`.
It's possible that, with `params Span`, we'd be able to achieve 90% of what we're trying to do without needing to build anything into the language itself.
And then there are languages like Swift, Python, Scala, and others that actually have a dedicated literal for lists, with varying degrees of flexibility.
#### Conclusions
No conclusions today. A smaller group will begin a deep dive into the space to flesh out the various questions and components of this proposal, and come
back to LDM with more research in the area.

View file

@ -0,0 +1,80 @@
# C# Language Design Meeting for November 3rd, 2021
## Agenda
1. [Name shadowing in local functions](#name-shadowing-in-local-functions)
2. [`params Span<T>`](#params-spant)
## Quote(s) of the Day
- "params Span<T>. This is an easy one, we should be out in 10 minutes."
- "For some value of 8"
- "Stack goes up. Stack goes down. You can't explain that."
- "That killed my monologue." "Did you mean 'evil villain monologue'?" "No one killed me during the monologue
therefore I can't be an evil villain." "No one kills the evil villain during the monologue, they just learn the evil plan and then escape to thwart it when the villain
leaves the implementation to his minions."
- "Solved the mystery: cat sitting on the spacebar"
## Discussion
### Name shadowing in local functions
https://github.com/dotnet/csharplang/discussions/5327
In C# 8, we relaxed the name shadowing requirements for lambdas and local functions, but we wanted to revisit the specific implementation strategy and whether we went too
far. In particular, we allowed a local function to _both_ capture a variable from an outer scope and shadow that variable in a nested scope inside the local function. This
_and_ in the previous sentence is the part we're concerned with: several members of the LDT didn't realize that we were agreeing to this in the original discussions on
shadowing.
There are two general models of shadowing in C#:
* Locals shadowing another local within the same function. This is expressly disallowed, and has been since C# 1.
* Locals shadowing a field. This is allowed, but the user can always get to the field version by using `this`. In the same method, it is possible to use the field without
the `this` qualifier and shadow it in a nested scope.
Shadowing between local functions while also capturing that local from the outer scope has similarities to both of these models: on the one hand, it's a local variable,
even if that variable has been potentially lifted to a field in an implicit closure. On the other hand, it's a variable from a scope outside the current function, just like
a field. This duality leads to conflicting resolution strategies, but we ultimately think this has more in common with locals shadowing other locals in the same method than
it does with locals shadowing fields.
The rules we wish we had implemented are: if a lambda or local function (or a nested lambda or local function) captures a variable from an outer method, it is an error to
shadow that variable. However, these rules have been out in the wild for nearly 3 years at this point, and we don't feel that the level of concern warrants a language change
or a warning wave to cover it. If an analyzer wants to implement such a suggestion (in dotnet/roslyn-analyzers, for example), they are free to, but the compiler itself will
not do so.
#### Conclusion
We regret that simultaneous capture/shadowing was allowed, and now it's too late to really fix it. Future features should keep this regret in mind and not use the way shadowing
in local functions/lambdas works as precedent.
### `params Span<T>`
https://github.com/dotnet/csharplang/issues/1757
`params Span<T>` is a feature that has been requested since we first implemented `Span<T>` in C# 7, and has a number of benefits. Today, users interested in performance with
`params` methods need to maintain overloads for common scenarios, such as passing 1-4 arguments (as we do for `Console.WriteLine`). These APIs are often not straightforward
to implement: if there was a simple common method they could all delegate to, that API would have been exposed in the first place.
Because of this focus, `params Span<T>` is an interesting first for the language: a `Span<T>`-based API that is explicitly targetted at the average C# developer, rather than
at someone who already knows what a `Span<T>` and why they would want to use it. There are also strong conflicting desires in the space that makes it very difficult to design
a single lowering strategy. We want to avoid allocations where possible, but we also want to avoid blowing out the stack when large numbers of parameters are passed to a
`params` method. This is particularly important in recursive code: Roslyn regularly runs into issues when inlining decisions can affect whether we are able to compile some
customer's code, as the compiler is an extremely recursive codebase.
At the same time, we also want to avoid being overly specific in the language specification on how this feature works. We would like to view the details as more an implementation
concern, much like we do with how lambdas are emitted, and avoid making strong guarantees about what will or won't stackalloc for what scenarios. As this is more targetted to
perf than lambdas are, it's possible that we can't be as glib in this space as we can for lambdas, but it's an aspiration for the feature.
Despite our desire to be less specific, we do think it's important to the initial design, and therefore the conception of who the feature is for, to have an implementation
strategy in mind. There are a number of different strategies that have been brought, with various pros and cons:
* Using a custom struct with a specific layout, and then making a span from the start of that struct.
* A shadow-stack that exists solely for the purpose of being able to give out and return stackalloc'd chunks of memory (think RAII stack space).
* Pushing variables onto the stack, then making a span from the start of those pushes (similar to the first approach, but without a dedicated type to be wrapped).
* `stackalloc` space that can be popped after a function is called.
* Not doing any kind of optimized IL in C#, and relying on the JIT to perform the escape analysis and translation from array to stackalloc when viable.
#### Conclusion
We'd like to see a group from the compiler and the runtime work through these proposed strategies and come up with "the way" or combination of ways that this will work, so we
can start to see how the user experience for the feature would actually work.

View file

@ -6,38 +6,102 @@ All schedule items must have a public issue or checked in proposal that can be l
## Schedule when convenient
- Remove restriction that interpolations within a non-verbatim interpolated string cannot contain new-lines. (cyrusn) https://github.com/dotnet/csharplang/issues/4935
## Recurring topics
- *Triage championed features and milestones*
- *Design review*
## Oct 06, 2021
## Dec 15, 2021
- "PGI" with MVPs
## Dec 1, 2021
## Oct 04, 2021
- Roles and extensions (Mads): https://github.com/dotnet/csharplang/issues/1711
- *Design review*
## Nov 10, 2021
## Sep 29, 2021
- Self-constraints (Tanner): https://github.com/dotnet/csharplang/pull/5387
## Sep 27, 2021
## Sep 22, 2021
## Sep 20, 2021
## Sep 15, 2021
- Feature specs and the C# Standard (Mads, Bill, Rex): https://github.com/dotnet/csharpstandard
- Permit variable declarations under disjunctive patterns (Julien): draft proposal https://github.com/dotnet/csharplang/pull/4592
# C# Language Design Notes for 2021
Overview of meetings and agendas for 2021
## Nov 3, 2021
[C# Language Design Notes for November 3rd, 2021](https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-11-03.md)
1. Name shadowing in local functions
2. `params Span<T>`
## Nov 1, 2021
[C# Language Design Notes for November 1st, 2021](https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-11-01.md)
1. Order of evaluation for Index and Range
2. Collection literals
## Oct 27, 2021
[C# Language Design Notes for October 27th, 2021](https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-10-27.md)
1. UTF-8 String Literals
2. Readonly modifiers for primary constructors
## Oct 25, 2021
[C# Language Design Notes for October 25th, 2021](https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-10-25.md)
1. Required members
2. Delegate type argument improvements
## Oct 20, 2021
[C# Language Design Notes for October 20th, 2021](https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-10-20.md)
1. Open questions in list patterns
1. Types that define both Length and Count
2. Slices that return null
2. Primary constructors
## Oct 13, 2021
[C# Language Design Notes for October 13th, 2021](https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-10-13.md)
1. Revisiting DoesNotReturn
2. Warning on lowercase type names
3. Length pattern backcompat
## Sep 22, 2021
[C# Language Design Notes for September 22nd, 2021](https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-09-22.md)
1. Open questions in list patterns
1. Breaking change confirmation
2. Positional patterns on ITuple
3. Slicing rules
4. Slice syntax recommendations
5. Other list pattern features
2. Nested members in `with` and object creation
3. CallerIdentityAttribute
4. Attributes on `Main` for top level programs
## Sep 20, 2021
[C# Language Design Notes for September 20th, 2021](https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-09-20.md)
1. Lambda breaking changes
2. Newlines in non-verbatim interpolated strings
3. Object initializer event hookup
4. Type alias improvements
## Sep 15, 2021
[C# Language Design Notes for September 15th, 2021](https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-09-15.md)
* Feedback from the C# standardization committee
* Permit pattern variables under disjunctive patterns
## Sep 13, 2021
[C# Language Design Notes for September 13th, 2021](https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-09-13.md)

View file

@ -35,8 +35,8 @@ convenient interpolation syntax.
We introduce a new handler pattern that can represent an interpolated string passed as an argument to a method. The simple English of the pattern is as follows:
When an _interpolated\_string\_expression_ is passed as an argument to a method, we look at the type of the parameter. If the parameter type has a constructor
that can be invoked with 2 int parameters, `literalLength` and `formattedCount`, optionally takes a parameter the receiver is convertible to,
and has an out parameter of the type of original method's parameter and that type has instance `AppendLiteral` and `AppendFormatted` methods that
that can be invoked with 2 int parameters, `literalLength` and `formattedCount`, optionally takes additional parameters specified by an attribute on the original
parameter, optionally has an out boolean trailing parameter, and the type of the original parameter has instance `AppendLiteral` and `AppendFormatted` methods that
can be invoked for every part of the interpolated string, then we lower the interpolation using that, instead of into a traditional call to
`string.Format(formatStr, args)`. A more concrete example is helpful for picturing this:
@ -61,16 +61,14 @@ public ref struct TraceLoggerParamsInterpolatedStringHandler
_logLevelEnabled = logger.EnabledLevel;
}
public bool AppendLiteral(string s)
public void AppendLiteral(string s)
{
// Store and format part as required
return true;
}
public bool AppendFormatted<T>(T t)
public void AppendFormatted<T>(T t)
{
// Store and format part as required
return true;
}
}
@ -97,15 +95,17 @@ logger.LogTrace($"{name} will never be printed because info is < trace!");
var name = "Fred Silberberg";
var receiverTemp = logger;
var handler = new TraceLoggerParamsInterpolatedStringHandler(literalLength: 47, formattedCount: 1, receiverTemp, out var handlerIsValid);
_ = handlerIsValid &&
handler.AppendFormatted(name) &&
if (handlerIsValid)
{
handler.AppendFormatted(name);
handler.AppendLiteral(" will never be printed because info is < trace!");
}
receiverTemp.LogTrace(handler);
```
Here, because `TraceLoggerParamsInterpolatedStringHandler` has a constructor with the correct parameters and returns the type the `LogTrace` call was expecting,
we say that the interpolated string has an implicit handler conversion to that parameter, and it lowers to the pattern shown above. The specese needed for this
is a bit complicated, and is expanded below.
Here, because `TraceLoggerParamsInterpolatedStringHandler` has a constructor with the correct parameters, we say that the interpolated string
has an implicit handler conversion to that parameter, and it lowers to the pattern shown above. The specese needed for this is a bit complicated,
and is expanded below.
The rest of this proposal will use `Append...` to refer to either of `AppendLiteral` or `AppendFormatted` in cases when both are applicable.
@ -291,10 +291,69 @@ as a _member\_access_ through type `T`.
* If no single-best method was found, the result of overload resolution is ambiguous, an error is produced, and no further steps are taken.
4. Final validation on `F` is performed.
* If any element of `A` occurred lexically after `i`, an error is produced and no further steps are taken.
* If any `A` requests the receiver of `F`, and `F` is an indexer being used as an _initializer\_target_ in a _member\_initializer_, then an error is reported and no further steps are taken.
Note: the resolution here intentionally do _not_ use the actual expressions passed as other arguments for `Argx` elements. We only consider the types post-conversion. This makes sure that we
don't have double-conversion issues, or unexpected cases where a lambda is bound to one delegate type when passed to `M1` and bound to a different delegate type when passed to `M`.
Note: We report an error for indexers uses as member initializers because of the order of evaluation for nested member initializers. Consider this code snippet:
```cs
var x1 = new C1 { C2 = { [GetString()] = { A = 2, B = 4 } } };
/* Lowering:
__c1 = new C1();
string argTemp = GetString();
__c1.C2[argTemp][1] = 2;
__c1.C2[argTemp][3] = 4;
Prints:
GetString
get_C2
get_C2
*/
string GetString()
{
Console.WriteLine("GetString");
return "";
}
class C1
{
private C2 c2 = new C2();
public C2 C2 { get { Console.WriteLine("get_C2"); return c2; } set { } }
}
class C2
{
public C3 this[string s]
{
get => new C3();
set { }
}
}
class C3
{
public int A
{
get => 0;
set { }
}
public int B
{
get => 0;
set { }
}
}
```
The arguments to `__c1.C2[]` are evaluated _before_ the receiver of the indexer. While we could come up with a lowering that works for this scenario (either by creating a temp for `__c1.C2`
and sharing it across both indexer invocations, or only using it for the first indexer invocation and sharing the argument across both invocations) we think that any lowering would be
confusing for what we believe is a pathological scenario. Therefore, we forbid the scenario entirely.
**~~Open Question~~**:
If we use a constructor instead of `Create`, we'd improve runtime codegen, at the expense of narrowing the pattern a bit.

View file

@ -1,106 +0,0 @@
# Lambda Attributes
## Summary
[summary]: #summary
Allow attributes to be applied to lambdas (and anonymous methods) and to lambda / anonymous method parameters, as they can be on regular methods.
## Motivation
[motivation]: #motivation
Two primary motivations:
1. To provide metadata visible to analyzers at compile-time.
2. To provide metadata visible to reflection and tooling at run-time.
As an example of (1):
For performance-sensitive code, it is helpful to be able to have an analyzer that flags when closures and delegates are being allocated for lambdas that close over state. Often a developer of such code will go out of his or her way to avoid capturing any state, so that the compiler can generate a static method and a cacheable delegate for the method, or the developer will ensure that the only state being closed over is `this`, allowing the compiler at least to avoid allocating a closure object. But, without language support for limiting what may be captured, it is all too easy to accidentally close over state. It would be valuable if a developer could annotate lambdas with attributes to indicate what state they're allowed to close over, for example:
```csharp
[CaptureNone] // can't close over any instance state
[CaptureThis] // can only capture `this` and no other instance state
[CaptureAny] // can close over any instance state
```
Then an analyzer can be written to flag when state is captured incorrectly, for example:
```csharp
var results = collection.Select([CaptureNone](i) => Process(item)); // Analyzer error: [CaptureNone] lambdas captures `this`
...
private U Process(T item) { ... }
```
## Detailed design
[design]: #detailed-design
- Using the same attribute syntax as on normal methods, attributes may be applied at the beginning of a lambda or anonymous method, for example:
```csharp
[SomeAttribute(...)] () => { ... }
[SomeAttribute(...)] delegate (int i) { ... }
```
- To avoid ambiguity as to whether an attribute applies to the lambda method or to one of the arguments, attributes may only be used when parens are used around any arguments, for example:
```csharp
[SomeAttribute] i => { ... } // ERROR
[SomeAttribute] (i) => { ... } // Ok
[SomeAttribute] (int i) => { ... } // Ok
```
- With anonymous methods, parens are not needed in order to apply an attribute to the method before the `delegate` keyword, for example:
```csharp
[SomeAttribute] delegate { ... } // Ok
[SomeAttribute] delegate (int i) => { ... } // Ok
```
- Multiple attributes may be applied, either via standard comma-delimited syntax or via full-attribute syntax, for example:
```csharp
[FirstAttribute, SecondAttribute] (i) => { ... } // Ok
[FirstAttribute] [SecondAttribute] (i) => { .... } // Ok
```
- Attributes may be applied to the parameters to an anonymous method or lambda, but only when parens are used around any arguments, for example:
```csharp
[SomeAttribute] i => { ... } // ERROR
([SomeAttribute] i) => { .... } // Ok
([SomeAttribute] int i) => { ... } // Ok
([SomeAttribute] i, [SomeOtherAttribute] j) => { ... } // Ok
```
- Multiple attributes may be applied to the parameters of an anonymous method or lambda, using either the comma-delimited or full-attribute syntax, for example:
```csharp
([FirstAttribute, SecondAttribute] i) => { ... } // Ok
([FirstAttribute] [SecondAttribute] i) => { ... } // Ok
```
- `return`-targeted attributes may also be used on lambdas, for example:
```csharp
([return: SomeAttribute] (i) => { ... }) // Ok
```
- The compiler outputs the attributes onto the generated method and arguments to those methods as it would for any other method.
## Drawbacks
[drawbacks]: #drawbacks
n/a
## Alternatives
[alternatives]: #alternatives
n/a
## Unresolved questions
[unresolved]: #unresolved-questions
n/a
## Design meetings
n/a

View file

@ -54,7 +54,13 @@ f = ([A] x) => x; // [A] x
f = ([A] ref int x) => x; // [A] x
```
Attributes are not supported for anonymous methods declared with `delegate { }` syntax.
Multiple attributes may be specified, either comma-separated within the same attribute list or as separate attribute lists.
```csharp
var f = [A1, A2][A3] () => { }; // ok
var g = ([A1][A2, A3] int x) => x; // ok
```
Attributes are not supported for _anonymous methods_ declared with `delegate { }` syntax.
```csharp
f = [A] delegate { return 1; }; // syntax error at 'delegate'
f = delegate ([A] int x) { return x; }; // syntax error at '['
@ -80,14 +86,6 @@ The changes proposed here are targeted at the `Delegate` driven scenario.
It should be valid to inspect the `MethodInfo` associated with a `Delegate` instance to determine the signature of the lambda expression or local function including any explicit attributes and additional metadata emitted by the compiler such as default parameters.
This allows teams such as ASP.NET to make available the same behaviors for lambdas and local functions as ordinary methods.
_Open issue: Should default values be supported for lambda expression parameters for completeness?_
### Well-known attributes
_Should `System.Diagnostics.ConditionalAttribute` be disallowed on lambda expressions since there are few scenarios where a lambda expression could be used conditionally?_
```csharp
([Conditional("DEBUG")] static (x, y) => Assert(x == y))(a, b); // ok?
```
## Explicit return type
An explicit return type may be specified before the parenthesized parameter list.
```csharp
@ -118,76 +116,125 @@ Func<object> f1 = string () => null; // error
Func<object?> f2 = object () => x; // warning
```
The parser should allow ref return types in assignment without parentheses.
The parser allows lambda expressions with `ref` return types within expressions without additional parentheses.
```csharp
Delegate d1 = (ref int () => x); // ok
Delegate d2 = ref int () => x; // ok
d = ref int () => x; // d = (ref int () => x)
F(ref int () => x); // F((ref int () => x))
```
`var` cannot be used as an explicit return type for lambda expressions.
```csharp
class var { }
d = var (var v) => v; // error: contextual keyword 'var' cannot be used as explicit lambda return type
d = @var (var v) => v; // ok
d = ref var (ref var v) => ref v; // error: contextual keyword 'var' cannot be used as explicit lambda return type
d = ref @var (ref var v) => ref v; // ok
```
## Natural (function) type
A lambda expression has a natural type if the parameters types are explicit and the return type is either explicit or can be inferred (see [inferred return type](https://github.com/dotnet/csharplang/blob/main/spec/expressions.md#inferred-return-type)).
An [_anonymous function_ expression](../../spec/expressions.md#anonymous-function-expressions) (a _lambda expression_ or an _anonymous method_) has a natural type if the parameters types are explicit and the return type is either explicit or can be inferred (see [inferred return type](../../spec/expressions.md#inferred-return-type)).
A method group has a natural type if all candidate methods in the method group have a common signature. (If the method group may include extension methods, the candidates include the containing type and all extension method scopes.)
A _method group_ has a natural type if all candidate methods in the method group have a common signature. (If the method group may include extension methods, the candidates include the containing type and all extension method scopes.)
The natural type of a lambda expression or method group is a _function_type_.
The natural type of an anonymous function expression or method group is a _function_type_.
A _function_type_ represents a method signature: the parameter types and ref kinds, and return type and ref kind.
Lambda expressions or method groups with the same signature have the same _function_type_.
Anonymous function expressions or method groups with the same signature have the same _function_type_.
_Function_types_ are used in a few specific contexts only:
- implicit and explicit conversions
- [method type inference](../../spec/expressions.md#type-inference) and [best common type](../../spec/expressions.md#finding-the-best-common-type-of-a-set-of-expressions)
- `var` initializers
A _function_type_ exists at compile time only: _function_types_ do not appear in source or metadata.
_Open issue: Should the function_type be available from the compiler API?_
### Conversions
From a _function_type_ `F` there are implicit _function_type_ conversions:
- To a _function_type_ `G` if the parameters and return types of `F` are variance-convertible to the parameters and return type of `G`
- To `System.MulticastDelegate` or base classes or interfaces of `System.MulticastDelegate`
- To `System.Linq.Expressions.Expression` or `System.Linq.Expressions.LambdaExpression`
Lambda expressions and method groups already have _conversions from expression_ to delegate types and expression tree types (see [anonymous function conversions](https://github.com/dotnet/csharplang/blob/main/spec/conversions.md#anonymous-function-conversions) and [method group conversions](https://github.com/dotnet/csharplang/blob/main/spec/conversions.md#method-group-conversions)). Those conversions are sufficient for converting to strongly-typed delegate types and expression tree types. The _function_type_ conversions above add _conversions from type_ to the base types only: `System.MulticastDelegate`, `System.Linq.Expressions.Expression`, etc.
Anonymous function expressions and method groups already have _conversions from expression_ to delegate types and expression tree types (see [anonymous function conversions](../../spec/conversions.md#anonymous-function-conversions) and [method group conversions](../../spec/conversions.md#method-group-conversions)). Those conversions are sufficient for converting to strongly-typed delegate types and expression tree types. The _function_type_ conversions above add _conversions from type_ to the base types only: `System.MulticastDelegate`, `System.Linq.Expressions.Expression`, etc.
There are no conversions to a _function_type_ from a type other than a _function_type_.
There are no explicit conversions for _function_types_ since _function_types_ cannot be referenced in source.
A conversion to `System.MulticastDelegate` or base type or interface realizes the lambda or method group as an instance of an appropriate delegate type.
A conversion to `System.MulticastDelegate` or base type or interface realizes the anonymous function or method group as an instance of an appropriate delegate type.
A conversion to `System.Linq.Expressions.Expression<TDelegate>` or base type realizes the lambda expression as an expression tree with an appropriate delegate type.
```csharp
Delegate d = delegate (object obj) { }; // Action<object>
Expression e = () => ""; // Expression<Func<string>>
object o = "".Clone; // Func<object>
```
_Function_type_ conversions are not implicit or explicit [standard conversions](../../spec/conversions.md#standard-conversions) and are not considered when determining whether a user-defined conversion operator is applicable to an anonymous function or method group.
From [evaluation of user defined conversions](../../spec/conversions.md#evaluation-of-user-defined-conversions):
> For a conversion operator to be applicable, it must be possible to perform a standard conversion ([Standard conversions](../../spec/conversions.md#standard-conversions)) from the source type to the operand type of the operator, and it must be possible to perform a standard conversion from the result type of the operator to the target type.
```csharp
class C
{
public static implicit operator C(Delegate d) { ... }
}
C c;
c = () => 1; // error: cannot convert lambda expression to type 'C'
c = (C)(() => 2); // error: cannot convert lambda expression to type 'C'
```
A warning is reported for an implicit conversion of a method group to `object`, since the conversion is valid but perhaps unintentional.
```csharp
Random r = new Random();
object obj;
obj = r.NextDouble; // warning: Converting method group to 'object'. Did you intend to invoke the method?
obj = (object)r.NextDouble; // ok
```
### Type inference
The existing rules for type inference are mostly unchanged (see [type inference](https://github.com/dotnet/csharplang/blob/main/spec/expressions.md#type-inference)). There are however a ***couple of changes*** below to specific phases of type inference.
The existing rules for type inference are mostly unchanged (see [type inference](../../spec/expressions.md#type-inference)). There are however a **couple of changes** below to specific phases of type inference.
#### First phase
The [first phase](https://github.com/dotnet/csharplang/blob/main/spec/expressions.md#the-first-phase) allows an anonymous function to bind to `Ti` even if `Ti` is not a delegate or expression tree type (perhaps a type parameter constrained to `System.Delegate` for instance).
The [first phase](../../spec/expressions.md#the-first-phase) allows an anonymous function to bind to `Ti` even if `Ti` is not a delegate or expression tree type (perhaps a type parameter constrained to `System.Delegate` for instance).
> For each of the method arguments `Ei`:
>
> * If `Ei` is an anonymous function ***and `Ti` is a delegate type or expression tree type***, an *explicit parameter type inference* is made from `Ei` to `Ti`
> * If `Ei` is an anonymous function **and `Ti` is a delegate type or expression tree type**, an *explicit parameter type inference* is made from `Ei` to `Ti` **and an *explicit return type inference* is made from `Ei` to `Ti`.**
> * Otherwise, if `Ei` has a type `U` and `xi` is a value parameter then a *lower-bound inference* is made *from* `U` *to* `Ti`.
> * Otherwise, if `Ei` has a type `U` and `xi` is a `ref` or `out` parameter then an *exact inference* is made *from* `U` *to* `Ti`.
> * Otherwise, no inference is made for this argument.
> #### **Explicit return type inference**
>
> **An *explicit return type inference* is made *from* an expression `E` *to* a type `T` in the following way:**
>
> * **If `E` is an anonymous function with explicit return type `Ur` and `T` is a delegate type or expression tree type with return type `Vr` then an *exact inference* ([Exact inferences](../../spec/expressions.md#exact-inferences)) is made *from* `Ur` *to* `Vr`.**
#### Fixing
[Fixing](https://github.com/dotnet/csharplang/blob/main/spec/expressions.md#fixing) ensures other conversions are preferred over _function_type_ conversions. (Lambda expressions and method group expressions only contribute to lower bounds so handling of _function_types_ is needed for lower bounds only.)
[Fixing](../../spec/expressions.md#fixing) ensures other conversions are preferred over _function_type_ conversions. (Lambda expressions and method group expressions only contribute to lower bounds so handling of _function_types_ is needed for lower bounds only.)
> An *unfixed* type variable `Xi` with a set of bounds is *fixed* as follows:
>
> * The set of *candidate types* `Uj` starts out as the set of all types in the set of bounds for `Xi` ***where function types are ignored in lower bounds if there any types that are not function types***.
> * The set of *candidate types* `Uj` starts out as the set of all types in the set of bounds for `Xi` **where function types are ignored in lower bounds if there any types that are not function types**.
> * We then examine each bound for `Xi` in turn: For each exact bound `U` of `Xi` all types `Uj` which are not identical to `U` are removed from the candidate set. For each lower bound `U` of `Xi` all types `Uj` to which there is *not* an implicit conversion from `U` are removed from the candidate set. For each upper bound `U` of `Xi` all types `Uj` from which there is *not* an implicit conversion to `U` are removed from the candidate set.
> * If among the remaining candidate types `Uj` there is a unique type `V` from which there is an implicit conversion to all the other candidate types, then `Xi` is fixed to `V`.
> * Otherwise, type inference fails.
### Best common type
Best common type is defined in terms of type inference (see [finding the best common type](https://github.com/dotnet/csharplang/blob/main/spec/expressions.md#finding-the-best-common-type-of-a-set-of-expressions)) so the changes above apply to best common type as well.
[Best common type](../../spec/expressions.md#finding-the-best-common-type-of-a-set-of-expressions) is defined in terms of type inference so the type inference changes above apply to best common type as well.
```csharp
var fs = new[] { (string s) => s.Length; (string s) => int.Parse(s) } // Func<string, int>[]
```
### `var`
Lambda expressions and method groups with natural types can be used as initializers in `var` declarations.
Anonymous functions and method groups with function types can be used as initializers in `var` declarations.
```csharp
var f1 = () => default; // error: cannot infer type
var f2 = x => { }; // error: cannot infer type
var f3 = x => x; // error: cannot infer type
var f4 = () => 1; // System.Func<int>
var f5 = string () => null; // System.Func<string>
var f1 = () => default; // error: cannot infer type
var f2 = x => x; // error: cannot infer type
var f3 = () => 1; // System.Func<int>
var f4 = string () => null; // System.Func<string>
var f5 = delegate (object o) { }; // System.Action<object>
static void F1() { }
static void F1<T>(this T t) { }
@ -198,52 +245,47 @@ var f7 = "".F1; // System.Action
var f8 = F2; // System.Action<string>
```
Function types are not used in assignments to discards.
```csharp
d = () => 0; // ok
_ = () => 1; // error
```
### Delegate types
The delegate type for the lambda or method group and parameter types `P1, ..., Pn` and return type `R` is:
- if any parameter or return value is not by value, or there are more than 16 parameters, or any of the parameter types or return are not valid type arguments (say, `(int* p) => { }`), then the delegate is a synthesized `internal` anonymous delegate type with signature that matches the lambda or method group, and with parameter names `arg1, ..., argn` or `arg` if a single parameter;
The delegate type for the anonymous function or method group with parameter types `P1, ..., Pn` and return type `R` is:
- if any parameter or return value is not by value, or there are more than 16 parameters, or any of the parameter types or return are not valid type arguments (say, `(int* p) => { }`), then the delegate is a synthesized `internal` anonymous delegate type with signature that matches the anonymous function or method group, and with parameter names `arg1, ..., argn` or `arg` if a single parameter;
- if `R` is `void`, then the delegate type is `System.Action<P1, ..., Pn>`;
- otherwise the delegate type is `System.Func<P1, ..., Pn, R>`.
The compiler may allow more signatures to bind to `System.Action<>` and `System.Func<>` types in the future (if `ref struct` types are allowed type arguments for instance).
_Open issue: Should the compiler bind to a matching `System.Action<>` or `System.Func<>` type regardless of arity and synthesize a delegate type otherwise? If so, should the compiler warn if the expected delegate types are missing?_
`modopt()` or `modreq()` in the method group signature are ignored in the corresponding delegate type.
If two lambda expressions or method groups in the same compilation require synthesized delegate types with the same parameter types and modifiers and the same return type and modifiers, the compiler will use the same synthesized delegate type.
If two anonymous functions or method groups in the same compilation require synthesized delegate types with the same parameter types and modifiers and the same return type and modifiers, the compiler will use the same synthesized delegate type.
### Overload resolution
Overload resolution already prefers binding to a strongly-typed delegate over `System.Delegate`, and prefers binding a lambda expression to a strongly-typed `System.Linq.Expressions.Expression<TDelegate>` over the corresponding strongly-typed delegate `TDelegate`.
Overload resolution will be updated to prefer binding a lambda expression to `System.Linq.Expressions.Expression` over `System.Delegate`. A strongly-typed delegate will still be preferred over the weakly-typed `System.Linq.Expressions.Expression` however.
[Better function member](../../spec/expressions.md#better-function-member) is updated to prefer members where none of the conversions and none of the type arguments involved inferred types from lambda expressions or method groups.
```csharp
static void Invoke(Func<string> f) { }
static void Invoke(Delegate d) { }
static void Invoke(Expression e) { }
> #### Better function member
> ...
> Given an argument list `A` with a set of argument expressions `{E1, E2, ..., En}` and two applicable function members `Mp` and `Mq` with parameter types `{P1, P2, ..., Pn}` and `{Q1, Q2, ..., Qn}`, `Mp` is defined to be a ***better function member*** than `Mq` if
>
> 1. **for each argument, the implicit conversion from `Ex` to `Px` is not a _function_type_conversion_, and**
> * **`Mp` is a non-generic method or `Mp` is a generic method with type parameters `{X1, X2, ..., Xp}` and for each type parameter `Xi` the type argument is inferred from an expression or from a type other than a _function_type_, and**
> * **for at least one argument, the implicit conversion from `Ex` to `Qx` is a _function_type_conversion_, or `Mq` is a generic method with type parameters `{Y1, Y2, ..., Yq}` and for at least one type parameter `Yi` the type argument is inferred from a _function_type_, or**
> 2. for each argument, the implicit conversion from `Ex` to `Qx` is not better than the implicit conversion from `Ex` to `Px`, and for at least one argument, the conversion from `Ex` to `Px` is better than the conversion from `Ex` to `Qx`.
static string GetString() => "";
static int GetInt() => 0;
[Better conversion from expression](../../spec/expressions.md#better-conversion-from-expression) is updated to prefer conversions that did not involve inferred types from lambda expressions or method groups.
Invoke(GetString); // Invoke(Func<string>) [unchanged]
Invoke(GetInt); // Invoke(Delegate) [new]
Invoke(() => ""); // Invoke(Func<string>) [unchanged]
Invoke(() => 0); // Invoke(Expression) [new]
```
_Inferring a delegate type for lambdas and method groups will result in some breaking changes in overload resolution: see [issues/4674](https://github.com/dotnet/csharplang/issues/4674)._
## Direct invocation
Lambda expressions may be invoked directly.
The compiler will generate a call to the underlying method without generating a delegate instance or synthesizing a delegate type.
Directly invoked lambda expressions do not require explicit parameter types.
```csharp
int zero = ((int x) => x)(0); // ok
int one = (x => x)(1); // ok
```
_Direct invocation will be addressed separately since the feature does not depend on other changes in this proposal: see [issues/4748](https://github.com/dotnet/csharplang/issues/4748)._
> #### Better conversion from expression
>
> Given an implicit conversion `C1` that converts from an expression `E` to a type `T1`, and an implicit conversion `C2` that converts from an expression `E` to a type `T2`, `C1` is a ***better conversion*** than `C2` if:
> 1. **`C1` is not a _function_type_conversion_ and `C2` is a _function_type_conversion_, or**
> 2. `E` is a non-constant _interpolated\_string\_expression_, `C1` is an _implicit\_string\_handler\_conversion_, `T1` is an _applicable\_interpolated\_string\_handler\_type_, and `C2` is not an _implicit\_string\_handler\_conversion_, or
> 3. `E` does not exactly match `T2` and at least one of the following holds:
> * `E` exactly matches `T1` ([Exactly matching Expression](../../spec/expressions.md#exactly-matching-expression))
> * `T1` is a better conversion target than `T2` ([Better conversion target](../../spec/expressions.md#better-conversion-target))
## Syntax
@ -264,12 +306,15 @@ lambda_parameter
;
```
## Design meetings
## Open issues
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-03-03.md
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-04-12.md#lambda-improvements
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-04-21.md#inferred-types-for-lambdas-and-method-groups
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-05-10.md
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-06-02.md#lambda-return-type-parsing
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-06-21.md#open-questions-for-lambda-return-types
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-07-12.md
Should default values be supported for lambda expression parameters for completeness?
Should `System.Diagnostics.ConditionalAttribute` be disallowed on lambda expressions since there are few scenarios where a lambda expression could be used conditionally?
```csharp
([Conditional("DEBUG")] static (x, y) => Assert(x == y))(a, b); // ok?
```
Should the _function_type_ be available from the compiler API, in addition to the resulting delegate type?
Currently, the inferred delegate type uses `System.Action<>` or `System.Func<>` when parameter and return types are valid type arguments _and_ there are no more than 16 parameters, and if the expected `Action<>` or `Func<>` type is missing, an error is reported. Instead, should the compiler use `System.Action<>` or `System.Func<>` regardless of arity? And if the expected type is missing, synthesize a delegate type otherwise?

View file

@ -60,9 +60,6 @@ The difference is, for those well known types, the _builder types_ are also know
`Builder.Create()` is invoked to create an instance of the _builder type_.
If the state machine is implemented as a `struct`, then `builder.SetStateMachine(stateMachine)` is called
with a boxed instance of the state machine that the builder can cache if necessary.
`builder.Start(ref stateMachine)` is invoked to associate the builder with compiler-generated state machine instance.
The builder must call `stateMachine.MoveNext()` either in `Start()` or after `Start()` has returned to advance the state machine.
After `Start()` returns, the `async` method calls `builder.Task` for the task to return from the async method.
@ -74,9 +71,13 @@ If an exception is thrown in the state machine, `builder.SetException(exception)
If the state machine reaches an `await expr` expression, `expr.GetAwaiter()` is invoked.
If the awaiter implements `ICriticalNotifyCompletion` and `IsCompleted` is false,
the state machine invokes `builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine)`.
`AwaitUnsafeOnCompleted()` should call `awaiter.OnCompleted(action)` with an action that calls `stateMachine.MoveNext()`
`AwaitUnsafeOnCompleted()` should call `awaiter.UnsafeOnCompleted(action)` with an `Action` that calls `stateMachine.MoveNext()`
when the awaiter completes. Similarly for `INotifyCompletion` and `builder.AwaitOnCompleted()`.
`SetStateMachine(IAsyncStateMachine)` is called by the compiler-generated `IAsyncStateMachine` implementation.
That can be used to identify the instance of the builder associated with a state machine instance, particularly for cases where the state machine is implemented as a value type:
if the builder calls `stateMachine.SetStateMachine(stateMachine)`, the `stateMachine` will call `builder.SetStateMachine(stateMachine)` on the _builder instance associated with `stateMachine`_.
## Overload Resolution
Overload resolution is extended to recognize _task types_ in addition to `Task` and `Task<T>`.

View file

@ -1,10 +1,5 @@
# Async Main
* [x] Proposed
* [ ] Prototype
* [ ] Implementation
* [ ] Specification
## Summary
[summary]: #summary

View file

@ -1,10 +1,5 @@
# pattern-matching with generics
* [x] Proposed
* [ ] Prototype:
* [ ] Implementation:
* [ ] Specification:
## Summary
[summary]: #summary

View file

@ -1,10 +1,5 @@
# Target-typed "default" literal
* [x] Proposed
* [x] Prototype
* [x] Implementation
* [ ] Specification
## Summary
[summary]: #summary

View file

@ -1,10 +1,5 @@
# private protected
* [x] Proposed
* [x] Prototype: [Complete](https://github.com/dotnet/roslyn/blob/master/docs/features/private-protected.md)
* [x] Implementation: [Complete](https://github.com/dotnet/roslyn/blob/master/docs/features/private-protected.md)
* [x] Specification: [Complete](#detailed-design)
## Summary
[summary]: #summary

View file

@ -1,10 +1,5 @@
# Readonly references
* [x] Proposed
* [x] Prototype
* [x] Implementation: Started
* [ ] Specification: Not Started
## Summary
[summary]: #summary

View file

@ -1,10 +1,5 @@
# Async Streams
* [x] Proposed
* [x] Prototype
* [ ] Implementation
* [ ] Specification
## Summary
[summary]: #summary

View file

@ -1,10 +1,5 @@
# default interface methods
* [x] Proposed
* [ ] Prototype: [In progress](https://github.com/dotnet/roslyn/blob/master/docs/features/DefaultInterfaceImplementation.md)
* [ ] Implementation: None
* [ ] Specification: In progress, below
## Summary
[summary]: #summary

View file

@ -1,10 +1,5 @@
# null coalescing assignment
* [x] Proposed
* [x] Prototype: Completed
* [x] Implementation: Completed
* [x] Specification: Below
## Summary
[summary]: #summary

View file

@ -154,6 +154,12 @@ The new indexer will be implemented by converting the argument of type `Index` i
- When the argument is of the form `^expr2` and the type of `expr2` is `int`, it will be translated to `receiver.Length - expr2`.
- Otherwise, it will be translated as `expr.GetOffset(receiver.Length)`.
Regardless of the specific conversion strategy, the order of evaluation should be equivalent to the following:
1. `receiver` is evaluated;
2. `expr` is evaluated;
3. `length` is evaluated, if needed;
4. the `int` based indexer is invoked.
This allows for developers to use the `Index` feature on existing types without the need for modification. For example:
``` csharp
@ -221,7 +227,13 @@ This value will be re-used in the calculation of the second `Slice` argument. Wh
- When `expr` is of the form `expr1..` (where `expr1` can be omitted), then it will be emitted as `receiver.Length - start`.
- Otherwise, it will be emitted as `expr.End.GetOffset(receiver.Length) - start`.
The `receiver`, `Length`, and `expr` expressions will be spilled as appropriate to ensure any side effects are only executed once. For example:
Regardless of the specific conversion strategy, the order of evaluation should be equivalent to the following:
1. `receiver` is evaluated;
2. `expr` is evaluated;
3. `length` is evaluated, if needed;
4. the `Slice` method is invoked.
The `receiver`, `expr`, and `length` expressions will be spilled as appropriate to ensure any side effects are only executed once. For example:
``` csharp
class Collection {

View file

@ -135,8 +135,6 @@ expr!.M();
_ = a?.b!.c;
```
The `primary_expression` and `null_conditional_operations_no_suppression` must be of a nullable type.
The postfix `!` operator has no runtime effect - it evaluates to the result of the underlying expression. Its only role is to change the null state of the expression to "not null", and to limit warnings given on its use.
### Nullable compiler directives

View file

@ -69,7 +69,12 @@ As a result, an error is produced for something like `case [.., p]: case [p]:` b
If a slice subpattern matches a list or a length value, subpatterns are treated as if they were a direct subpattern of the containing list. For instance, `[..[1, 2, 3]]` subsumes a pattern of the form `[1, 2, 3]`.
`Length` or `Count` properties are assumed to always return a non-negative value, if and only if the type is *indexable*. For instance, the pattern `{ Length: -1 }` can never match an array. The behavior of a pattern-matching operation is undefined if this assumption doesn't hold.
The following assumptions are made on the members being used:
- The property that makes the type *countable* is assumed to always return a non-negative value, if and only if the type is *indexable*. For instance, the pattern `{ Length: -1 }` can never match an array.
- The member that makes the type *sliceable* is assumed to be well-behaved, that is, the return value is never null and that it is a proper subslice of the containing list.
The behavior of a pattern-matching operation is undefined if any of the above assumptions doesn't hold.
#### Lowering

View file

@ -0,0 +1,40 @@
# Allow new-lines in all interpolations
* [x] Proposed
* [x] Implementation: https://github.com/dotnet/roslyn/pull/56853
* [x] Specification: this file.
## Summary
[summary]: #summary
The language today non-verbatim and verbatim interpolated strings (`$""` and `$@""` respectively). The primary *sensible* difference for these is that a non-verbatim interpolated string works like a normal string and cannot contain newlines in its text segments, and must instead use escapes (like `\r\n`). Conversely, a verbatim interpolated string can contain newlines in its text segments (like a verbatim string), and doesn't escape newlines or other character (except for `""` to escape a quote itself).
This is all reasonable and will not change with this proposal.
What is unreasonable today is that we extend the restriction on 'no newlines' in a non-verbatim interpolated string *beyond* its text segments into the *interpolations* themselves. This means, for example, that you cannot write the following:
```c#
var v = $"Count is\t: { this.Is.A.Really(long(expr))
.That.I.Should(
be + able)[
to.Wrap()] }.";
```
Ultimately, the 'interpolation must be on a single line itself' rule is just a restriction of the current implementation. That restriction really isn't necessary, and can be annoying, and would be fairly trivial to remove (see work https://github.com/dotnet/roslyn/pull/54875 to show how). In the end, all it does is force the dev to place things on a single line, or force them into a verbatim interpolated string (both of which may be unpalatable).
The interpolation expressions themselves are not text, and shouldn't be beholden to any escaping/newline rules therin.
## Specification change
```diff
single_regular_balanced_text_character
- : '<Any character except / (U+002F), @ (U+0040), \" (U+0022), $ (U+0024), ( (U+0028), ) (U+0029), [ (U+005B), ] (U+005D), { (U+007B), } (U+007D) and new_line_character>'
- | '</ (U+002F), if not directly followed by / (U+002F) or * (U+002A)>'
+ : <Any character except @ (U+0040), \" (U+0022), $ (U+0024), ( (U+0028), ) (U+0029), [ (U+005B), ] (U+005D), { (U+007B), } (U+007D)>
+ | comment
;
```
## LDM Discussions
https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-09-20.md

View file

@ -0,0 +1,88 @@
# Primary constructors in non-record classes and structs
* [x] Proposed
* [ ] Prototype: Not started
* [ ] Implementation: Not started
* [ ] Specification: Not started
## Summary
[summary]: #summary
Primary constructors, currently only available on [record types](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-9.0/records.md#primary-constructor), will be generalized to non-record classes and structs. They have the following differences in behavior from primary constructors in records:
- Instead of public members, a private field is generated for each parameter of the primary constructor.
- If the field is unreferenced (as a field) within the body of the class or struct declaration, it is not emitted. (The parameter can still be used in e.g. initializers).
- No corresponding deconstructor is generated.
## Motivation
[motivation]: #motivation
The ability of a class or struct in C# to have more than one constructor provides for generality, but at the expense of some tedium in the declaration syntax, because the constructor input and the class state need to be cleanly separated.
Primary constructors put the parameters of one constructor in scope for the whole class to be used for initialization or directly as object state. The trade-off is that any other constructors must call through the primary constructor.
``` c#
public class C(int i, string s)
{
public int I { get; set; } = i; // i used for initialization
public string S // s used directly in function members
{
get => s;
set => s = value ?? throw new NullArgumentException(nameof(X));
}
}
```
## Detailed design
[design]: #detailed-design
*Note*: Any similarity with the specification of [primary constructors in records](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-9.0/records.md#primary-constructor) is entirely intentional.
Class and struct declarations are augmented to allow a parameter list on the type name and and argument list on the base class:
``` antlr
class_declaration
: attributes? class_modifier* 'partial'? 'class' identifier type_parameter_list?
parameter_list? class_base? type_parameter_constraints_clause* class_body ';'?
;
class_base
: ':' class_type argument_list?
| ':' interface_type_list
| ':' class_type argument_list? ',' interface_type_list
;
struct_declaration
: attributes? struct_modifier* 'partial'? 'struct' identifier type_parameter_list?
parameter_list? struct_interfaces? type_parameter_constraints_clause* struct_body ';'?
;
```
It is an error for a `class_base` to have an `argument_list` if the enclosing `class_declaration` does not contain a `parameter_list`. At most one partial type declaration of a partial class or struct may provide a `parameter_list`. The parameters in the `parameter_list` must all be value parameters.
A class or struct with a `parameter_list` has an implicit public constructor whose signature corresponds to the value parameters of the type declaration. This is called the ***primary constructor*** for the type, and causes the implicitly declared parameterless constructor, if present, to be suppressed. It is an error to have a primary constructor and a constructor with the same signature already present in the type declaration.
At runtime the primary constructor
1. executes the instance initializers appearing in the class or struct body
2. invokes the base class constructor with the arguments provided in the `class_base` clause, if present
If a class or struct has a primary constructor, any user-defined constructor, except "copy constructor" must have an explicit `this` constructor initializer.
Parameters of the primary constructor as well as members of the record are in scope within the `argument_list`
of the `class_base` clause and within initializers of instance fields or properties. Instance members would
be an error in these locations (similar to how instance members are in scope in regular constructor initializers today, but an error to use), but the parameters of the primary constructor would be in scope and useable and would shadow members. Static members would also be usable, similar to how base calls and initializers work in ordinary constructors today.
A warning is produced if a parameter of the primary constructor is not read.
Expression variables declared in the `argument_list` are in scope within the `argument_list`. The same shadowing rules as within an argument list of a regular constructor initializer apply.
For each parameter in the `parameter_list`, if the type declaration does not directly contain a property or field declaration of the same name as the parameter, and if any expression within the body of the type declaration would reference such a member, then a private field is implicitly declared with the same name and type as the parameter.
The field is initialized to the value of the corresponding primary constructor parameter. Attributes can be applied to the synthesized field by using `field:` targets for attributes syntactically applied to the corresponding record parameter.
Unlike primary constructors in records, no deconstructor is generated.

View file

@ -1,4 +1,4 @@
# Simplified Null Argument Checking
# Parameter Null Checking
## Summary
This proposal provides a simplified syntax for validating method arguments are not `null` and throwing
@ -16,17 +16,17 @@ of it. The syntax can be used independent of `#nullable` directives.
## Detailed Design
### Null validation parameter syntax
The bang operator, `!`, can be positioned after a parameter name in a parameter list and this will cause the C#
compiler to emit standard `null` checking code for that parameter. This is referred to as `null` validation parameter
The bang-bang operator, `!!`, can be positioned after a parameter name in a parameter list and this will cause the C#
compiler to emit `null` checking code for that parameter. This is referred to as `null` validation parameter
syntax. For example:
``` csharp
void M(string name!) {
void M(string name!!) {
...
}
```
Will be translated into:
Will be translated into code similar to the following:
``` csharp
void M(string name) {
@ -37,8 +37,12 @@ void M(string name) {
}
```
The implementation behavior must be that if the parameter is null, it creates and throws an `ArgumentNullException` with the parameter name as a constructor argument. The implementation is free to use any strategy that achieves this. This could result in observable differences between different compliant implementations, such as whether calls to helper methods are present above the call to the method with the null-checked parameter in the exception stack trace.
We make these allowances because parameter null checks are used frequently in libraries with tight performance and size constraints. For example, to optimize code size, inlining, etc., the implementation may use helper methods to perform the null check a la the [ArgumentNullException.ThrowIfNull](https://github.com/dotnet/runtime/blob/1d08e154b942a41e72cbe044e01fff8b13c74496/src/libraries/System.Private.CoreLib/src/System/ArgumentNullException.cs#L56-L69) methods.
The generated `null` check will occur before any developer authored code in the method. When multiple parameters contain
the `!` operator then the checks will occur in the same order as the parameters are declared.
the `!!` operator then the checks will occur in the same order as the parameters are declared.
``` csharp
void M(string p1, string p2) {
@ -53,12 +57,12 @@ void M(string p1, string p2) {
```
The check will be specifically for reference equality to `null`, it does not invoke `==` or any user defined operators.
This also means the `!` operator can only be added to parameters whose type can be tested for equality against `null`.
This also means the `!!` operator can only be added to parameters whose type can be tested for equality against `null`.
This means it can't be used on a parameter whose type is known to be a value type.
``` csharp
// Error: Cannot use ! on parameters who types derive from System.ValueType
void G<T>(T arg!) where T : struct {
// Error: Cannot use !! on parameters who types derive from System.ValueType
void G<T>(T arg!!) where T : struct {
}
```
@ -73,7 +77,7 @@ For example:
``` csharp
class C {
string field = GetString();
C(string name!): this(name) {
C(string name!!): this(name) {
...
}
}
@ -101,7 +105,7 @@ parameter syntax that lacks parens.
``` csharp
void G() {
// An identity lambda which throws on a null input
Func<string, string> s = x! => x;
Func<string, string> s = x!! => x;
}
```
@ -111,7 +115,7 @@ or `async` iterators.
``` csharp
class Iterators {
IEnumerable<char> GetCharacters(string s!) {
IEnumerable<char> GetCharacters(string s!!) {
foreach (var c in s) {
yield return c;
}
@ -124,7 +128,7 @@ class Iterators {
}
```
The `!` operator can only be used for parameter lists which have an associated method body. This
The `!!` operator can only be used for parameter lists which have an associated method body. This
means it cannot be used in an `abstract` method, `interface`, `delegate` or `partial` method
definition.
@ -153,16 +157,16 @@ is instantiated as a value type the code will be evaluated as `false`. For cases
code will do a proper `is null` check.
### Intersection with Nullable Reference Types
Any parameter which has a `!` operator applied to it's name will start with the nullable state being not `null`. This is
Any parameter which has a `!!` operator applied to it's name will start with the nullable state being not `null`. This is
true even if the type of the parameter itself is potentially `null`. That can occur with an explicitly nullable type,
such as say `string?`, or with an unconstrained type parameter.
When a `!` syntax on parameters is combined with an explicitly nullable type on the parameter then a warning will
When a `!!` syntax on parameters is combined with an explicitly nullable type on the parameter then a warning will
be issued by the compiler:
``` csharp
void WarnCase<T>(
string? name!, // Warning: combining explicit null checking with a nullable type
string? name!!, // Warning: combining explicit null checking with a nullable type
T value1 // Okay
)
```
@ -174,7 +178,7 @@ None
### Constructors
The code generation for constructors means there is a small, but observable, behavior change when moving from standard
`null` validation today and the `null` validation parameter syntax (`!`). The `null` check in standard validation
`null` validation today and the `null` validation parameter syntax (`!!`). The `null` check in standard validation
occurs after both field initializers and any `base` or `this` calls. This means a developer can't necessarily migrate
100% of their `null` validation to the new syntax. Constructors at least require some inspection.
@ -183,7 +187,7 @@ logical that the `null` check run before any logic in the constructor does. Can
are discovered.
### Warning when mixing ? and !
There was a lengthy discussion on whether or not a warning should be issued when the `!` syntax is applied to a
There was a lengthy discussion on whether or not a warning should be issued when the `!!` syntax is applied to a
parameter which is explicitly typed to a nullable type. On the surface it seems like a nonsensical declaration by
the developer but there are cases where type hierarchies could force developers into such a situation.
@ -203,7 +207,7 @@ abstract class C2 : C1 {
// Assembly3
abstract class C3 : C2 {
protected override void M(object o!) {
protected override void M(object o!!) {
...
}
}
@ -232,7 +236,7 @@ following to eliminate it:
``` csharp
// Assembly3
abstract class C3 : C2 {
protected override void M(object? o!) {
protected override void M(object? o!!) {
...
}
}
@ -241,15 +245,15 @@ abstract class C3 : C2 {
At this point the author of Assembly3 has a few choices:
- They can accept / suppress the warning about `object?` and `object` mismatch.
- They can accept / suppress the warning about `object?` and `!` mismatch.
- They can just remove the `null` validation check (delete `!` and do explicit checking)
- They can accept / suppress the warning about `object?` and `!!` mismatch.
- They can just remove the `null` validation check (delete `!!` and do explicit checking)
This is a real scenario but for now the idea is to move forward with the warning. If it turns out the warning happens
more frequently than we anticipate then we can remove it later (the reverse is not true).
### Implicit property setter arguments
The `value` argument of a parameter is implicit and does not appear in any parameter list. That means it cannot be a
target of this feature. The property setter syntax could be extended to include a parameter list to allow the `!`
target of this feature. The property setter syntax could be extended to include a parameter list to allow the `!!`
operator to be applied. But that cuts against the idea of this feature making `null` validation simpler. As such the
implicit `value` argument just won't work with this feature.

150
proposals/params-span.md Normal file
View file

@ -0,0 +1,150 @@
# `params Span<T>`
## Summary
Avoid heap allocation for implicit allocation of arrays in specific scenarios with `params` arguments.
## Motivation
`params` array parameters provide a convenient way to call a method that takes an arbitrary length list of arguments.
However, using an array type for the parameter means the compiler must implicitly allocate an array on the heap at each call site.
If we extend `params` types to include the `ref struct` types `Span<T>` and `ReadOnlySpan<T>`, where values of those types cannot escape the call stack, the array at the call site may be created on the stack instead.
And if we're extending `params` to other types, we could also allow `params IEnumerable<T>` to avoid allocating and copying collections at call sites that have an `IEnumerable<T>` rather than `T[]`.
The benefits of `params ReadOnlySpan<T>` and `params Span<T>` are primarily for new APIs. Existing commonly used APIs such as `Console.WriteLine()` and `StringBuilder.AppendFormat()` already have overloads that avoid array allocations for common cases and those overloads would need to be retained for backward compatibility.
```csharp
public static class Console
{
public static void WriteLine(string value);
public static void WriteLine(string format, object arg0);
public static void WriteLine(string format, object arg0, object arg1);
public static void WriteLine(string format, object arg0, object arg1, object arg2);
public static void WriteLine(string format, params object[] arg);
}
```
## Detailed design
### Extending `params`
`params` parameters will be supported with types `Span<T>`, `ReadOnlySpan<T>`, and `IEnumerable<T>`.
A call in [_expanded form_](../spec/expressions.md#applicable-function-member) to a method with a `params T[]` or `params IEnumerable<T>` parameter will result in an array `T[]` allocated on the heap.
A call in [_expanded form_](../spec/expressions.md#applicable-function-member) to a method with a `params ReadOnlySpan<T>` or `params Span<T>` parameter will result in an array `T[]` created on the stack _if the `params` array is within limits (if any) set by the compiler_.
Otherwise the array will be allocated on the heap.
```csharp
Console.WriteLine(fmt, x, y, z); // WriteLine(string format, params ReadOnlySpan<object?> arg)
```
The compiler will report an error when compiling the method declaring the `params` parameter if the `ReadOnlySpan<T>` or `Span<T>` parameter value is returned from the method or assigned to an `out` parameter.
That ensures call-sites can create the underlying array on the stack and reuse the array across call-sites without concern for aliases.
A `params` parameter must be last parameter in the method signature.
Two overloads cannot differ by `params` modifier alone.
`params` parameters will be marked in metadata with a `System.ParamArrayAttribute` regardless of type.
### Overload resolution
Overload resolution will continue to prefer overloads that are applicable in [_normal form_](../spec/expressions.md#applicable-function-member) rather than [_expanded form_](../spec/expressions.md#applicable-function-member).
For overloads that are applicable in _expanded form_, [better function member](../spec/expressions.md#better-function-member) will be updated to prefer `params` types in a specific order:
> When performing this evaluation, if `Mp` or `Mq` is applicable in its expanded form, then `Px` or `Qx` refers to a parameter in the expanded form of the parameter list.
>
> In case the parameter type sequences `{P1, P2, ..., Pn}` and `{Q1, Q2, ..., Qn}` are equivalent (i.e. each `Pi` has an identity conversion to the corresponding `Qi`), the following tie-breaking rules are applied, in order, to determine the better function member.
>
> * If `Mp` is a non-generic method and `Mq` is a generic method, then `Mp` is better than `Mq`.
> * ...
> * **Otherwise, if both methods have `params` parameters and are applicable only in their expanded forms, and the `params` types are distinct types with equivalent element type (there is an identity conversion between element types), the more specific `params` type is the first of:**
> * **`ReadOnlySpan<T>`**
> * **`Span<T>`**
> * **`T[]`**
> * **`IEnumerable<T>`**
> * Otherwise if one member is a non-lifted operator and the other is a lifted operator, the non-lifted one is better.
> * Otherwise, neither function member is better.
### Array creation expressions
Array creation expressions that are target-typed to `ReadOnlySpan<T>` or `Span<T>` will be created on the stack _if the length of the array is a constant value within limits (if any) set by the compiler_.
Otherwise the array will be allocated on the heap.
```csharp
Span<int> s = new[] { i, j, k }; // int[] on the stack
WriteLine(fmt, new[] { x, y, z }); // object[] on the stack for WriteLine(string fmt, ReadOnlySpan<object> args);
```
### Array re-use
The compiler _may_ reuse an implicitly allocated array across multiple uses within a single thread executing a method:
- At the same call-site (within a loop) or
- At distinct call-sites if the lifetime of the spans do not overlap, and the array length is sufficient, and
- the element types are managed types that are considered identical by the runtime, or
- the element types are unmanaged types of the same size.
An implicitly allocated array may be reused regardless of whether the array was created on the stack or the heap.
### Lowering implicit allocation
For the `params` and array creation cases above that are target typed to `Span<T>` or `ReadOnlySpan<T>`, the compiler will lower the creation of spans using an efficient approach, specifically avoiding heap allocations when possible.
The exact details are still to be determined and may differ based on the target framework and runtime.
The guarantee the compiler gives is the span will be the expected size and will contain the expected items at any point in user code.
## Open issues
### Is `params Span<T>` necessary?
Is there a reason to support `params` parameters of type `Span<T>` in addition to `ReadOnlySpan<T>`? Is allowing mutation within the `params` method useful?
### Is `params IEnumerable<T>` necessary?
If the compiler allows `params ReadOnlySpan<T>`, then new APIs that require `params` could use `params ReadOnlySpan<T>` instead of `params T[]` because `T[]` is implicitly convertible to `ReadOnlySpan<T>`. And existing APIs could add a `params ReadOnlySpan<T>` overload where the existing `params T[]` simply delegates to the new overload.
There is no conversion from `IEnumerable<T>` to `ReadOnlySpan<T>` however, so allowing `params IEnumerable<T>` is essentially asking APIs to provide two overloads for `params` methods: `params ReadOnlySpan<T>` and `params IEnumerable<T>`.
Are scenarios for `params IEnumerable<T>` sufficiently compelling to justify that?
### Array limits
The compiler may use heuristics to determine when to fallback to heap allocation for the underlying data for spans.
If heuristics are necessary, experimentation should establish the limits we agree on.
### Lowering approach
We need to determine the particular approach used to lower `params` and array creation expressions to avoid heap allocation.
For instance, one potential approach to represent a `Span<T>` of constant length `N` is to synthesize a `struct` with `N` fields of type `T`
where the layout and alignment of the fields matches the alignment of elements in `T[]`, and create the `Span<T>` from a `ref` to the first field of the `struct`.
With that approach, `Console.WriteLine(fmt, x, y, z);` would be emitted as:
```csharp
[StructLayout(LayoutKind.Sequential)]
internal struct __ValueArray3<T> { public T Item1, Item2, Item3; };
var values = new __ValueArray3<object>() { Item1 = x, Item2 = y, Item3 = z };
var span = MemoryMarshal.CreateSpan(ref values.Item1, 3);
Console.WriteLine(fmt, (ReadOnlySpan<object>)span); // WriteLine(string format, params ReadOnlySpan<object?> arg)
```
Alternative approaches may require runtime support.
### Explicit `stackalloc`
Should we allow explicit stack allocation of arrays of managed types with `stackalloc` as well?
```csharp
public static ImmutableArray<TResult> Select<TSource, TResult>(this ImmutableArray<TSource> source, Func<TSource, TResult> map)
{
int n = source.Length;
Span<TResult> result = n <= 16 ? stackalloc TResult[n] : new TResult[n];
for (int i = 0; i < n; i++)
result[i] = map(source[i]);
return ImmutableArray.Create(result); // requires ImmutableArray.Create<T>([DoesNotEscape] ReadOnlySpan<T> items)
}
```
This would require runtime support for stack allocation of arrays of non-constant length and any type, and GC tracking of the elements.
Direct runtime support for stack allocation of arrays of managed types might be useful for lowering implicit allocation as well.
The GC does not currently track the lifetime of a `stackalloc` array so if the contents of the array have a shorter lifetime than the method, the compiler will need to zero the contents of the array so the lifetime of elements matches expectations.
### Opting out
Should we allow opt-ing out of _implicit allocation_ on the call stack?
Perhaps an attribute that can be applied to a method, type, or assembly.
## Related proposals
- https://github.com/dotnet/csharplang/issues/1757
- https://github.com/dotnet/csharplang/blob/main/proposals/format.md#extending-params

View file

@ -0,0 +1,73 @@
# Variable declarations under disjunctive patterns
## Summary
Allow variable declarations under `or` patterns and across `case` labels in a `switch` section.
## Motivation
This feature would reduce code duplication where we could use the same piece of code if either of patterns is satisfied. For instance:
```cs
if (e is (int x, 0) or (0, int x))
Use(x);
switch (e)
{
case (int x, 0):
case (0, int x):
Use(x);
break;
}
```
Instead of:
```cs
if (e is (int x1, 0))
Use(x1);
else if (e is (0, int x2))
Use(x2);
switch (e)
{
case (int x, 0):
Use(x);
break;
case (0, int x):
Use(x);
break;
}
```
## Detailed design
Variables *must* be redeclared under all disjuncitve patterns because assignment of such variables depend on the order of evaluation which is undefined in the context of pattern-matching.
- In a *disjunctive_pattern*, pattern variables declared on one side must be redeclared on the other side.
- In a *switch_section*, pattern variables declared under each case label must be redeclared under every other case label.
In any other case, variable declaration follows the usual scoping rules and is disallowed.
These names can reference either of variables based on the result of the pattern-matching at runtime. Under the hood, it's the same local being assigned in each pattern.
Redeclaring pattern variables is only permitted for variables of the same type.
## Unresolved questions
- How identical these types should be?
- Could we support variable declarations under `not` patterns?
```cs
if (e is not (int x, 0) and not (0, int x))
```
- Could we relax the scoping rules beyond pattern boundaries?
```cs
if (e is (int x, 0) || a is (0, int x))
```
- Could we relax the redeclaration requirement in a switch section?
```cs
case (int x, 0) a when Use(x, a): // ok
case (0, int x) b when Use(x, b): // ok
Use(x); // ok
Use(a); // error; not definitely assigned
Use(b); // error; not definitely assigned
break;
```

View file

@ -0,0 +1,113 @@
# Semi-auto-properties (a.k.a. `field` keyword in properties)
## Summary
Extend auto-properties to allow them to still have an automatically generated backing field, while still allowing for bodies to be provided for accessors. Auto-properties can also use a new contextual `field` keyword in their body to refer to the auto-prop field.
## Motivation
Standard auto-properties only allow for setting or getting the backing field directly, giving some control only by access modifying the accessor methods. Sometimes there is more need to have control over what happens when accessing an auto-property, without being confronted with all overhead of a standard property.
Two common scenarios are that you want to apply a constraint on the setter, ensuring the validity of a value. The other being raising an event that informs about the property going to be changed/having been changed.
In these cases by now you always have to create an instance field and write the whole property yourself. This not only adds a fair amount of code, but it also leaks the `field` into the rest of the type's scope, when it is often desirable to only have it be available to the bodies of the accessors.
## Specification changes
The following changes are to be made to [classes.md](https://github.com/dotnet/csharplang/blob/main/spec/classes.md):
```
### Automatically implemented properties
```
```diff
- An automatically implemented property (or ***auto-property*** for short), is a non-abstract non-extern
- property with semicolon-only accessor bodies. Auto-properties must have a get accessor and can optionally
- have a set accessor.
+ An automatically implemented property (or ***auto-property*** for short), is a non-abstract non-extern
+ property with either or both of:
```
```diff
- When a property is specified as an automatically implemented property, a hidden backing field is automatically
- available for the property, and the accessors are implemented to read from and write to that backing field. If
- the auto-property has no set accessor, the backing field is considered `readonly` ([Readonly fields](classes.md#readonly-fields)).
- Just like a `readonly` field, a getter-only auto-property can also be assigned to in the body of a constructor
- of the enclosing class. Such an assignment assigns directly to the readonly backing field of the property.
+ 1. an accessor with a semicolon-only body
+ 2. usage of the `field` contextual keyword ([Keywords](lexical-structure.md#keywords)) within the accessors or
+ expression body of the property. The `field` identifier is only considered the `field` keyword when there is
+ no existing symbol named `field` in scope at that location.
+
+ When a property is specified as an auto-property, a hidden, unnamed, backing field is automatically available for
+ the property. For auto-properties, any semicolon-only `get` accessor is implemented to read from, and any semicolon-only
+ `set` accessor to write to its backing field. The backing field can be referenced directly using the `field` keyword
+ within all accessors and within the property expression body. Because the field is unnamed, it cannot be used in a
+ `nameof` expression.
+
+ If the auto-property does not have a set accessor, the backing field can still be assigned to in the body of a
+ constructor of the enclosing class. Such an assignment assigns directly to the backing field of the property.
+
+ If the auto-property has only a semicolon-only get accessor, the backing field is considered `readonly` ([Readonly fields](classes.md#readonly-fields)).
+
+ An auto-property is not allowed to only have a single semicolon-only `set` accessor without a `get` accessor.
```
...
````diff
+The following example:
+```csharp
+// No 'field' symbol in scope.
+public class Point
+{
+ public int X { get; set; }
+ public int Y { get; set; }
+}
+```
+is equivalent to the following declaration:
+```csharp
+// No 'field' symbol in scope.
+public class Point
+{
+ public int X { get { return field; } set { field = value; } }
+ public int Y { get { return field; } set { field = value; } }
+}
+```
+which is equivalent to:
+```csharp
+// No 'field' symbol in scope.
+public class Point
+{
+ private int __x;
+ private int __y;
+ public int X { get { return __x; } set { __x = value; } }
+ public int Y { get { return __y; } set { __y = value; } }
+}
+```
+The following example:
+```csharp
+// No 'field' symbol in scope.
+public class LazyInit
+{
+ public string Value => field ??= ComputeValue();
+ private static string ComputeValue() { /*...*/ }
+}
+```
+is equivalent to the following declaration:
+```csharp
+// No 'field' symbol in scope.
+public class Point
+{
+ private string __value;
+ public string Value { get { return __value ??= ComputeValue(); } }
+ private static string ComputeValue() { /*...*/ }
+}
+```
````
Open LDM questions:
1. If a type does have an existing accessible `field` symbol in scope (like a field called `field`) should there be any way for an auto-prop to still use `field` internally to both create and refer to an auto-prop field. Under the current rules there is no way to do that. This is certainly unfortunate for those users, however this is ideally not a significant enough issue to warrant extra dispensation. The user, after all, can always still write out their properties like they do today, they just lose out from the convenience here in that small case.
## LDM history:
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-03-10.md#field-keyword
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-04-14.md#field-keyword

View file

@ -261,7 +261,7 @@ Called out above, but here's a list:
Not called out above:
- Confirm whether we would like to support use of static abstract methods declared in interfaces as operators in query expressions (https://github.com/dotnet/roslyn/issues/53796).
- Confirm the rules outlined in "Processing of user-defined implicit conversions" and "Processing of user-defined explicit conversions" sections above.
- Confirm the rules outlined in "Processing of user-defined implicit conversions" and "Processing of user-defined explicit conversions" sections above. Some feedback on the current rules https://github.com/dotnet/roslyn/issues/56753.
# Design meetings

View file

@ -0,0 +1,57 @@
# Allow using alias directive to reference any kind of Type
## Summary
Relax the [using_alias_directive](https://github.com/dotnet/csharplang/blob/main/spec/namespaces.md#using-alias-directives) to allow it to point at any sort of type, not just named types. This would support types not allowed today, like: tuple types, pointer types, array types, etc. For example, this would now be allowed:
```c#
using Point = (int x, int y);
```
## Motivation
For ages, C# has had the ability to introduce aliases for namespaces and named types (classes, delegated, interfaces, records and structs). This worked acceptably well as it provided a means to introduce non-conflicting names in cases where a normal named pulled in from `using_directive`s might be ambiguous, and it allowed a way to provide a simpler name when dealing with complex generic types. However, the rise of additional complex type symbols in the language has caused more use to arise where aliases would be valuable but are currently not allowed. For example, both tuples and function-pointers often can have large and complex regular textual forms that can be painful to continually write out, and a burden to try to read. Aliases would help in these cases by giving a short, developer-provided, name that can then be used in place of those full structural forms.
## Detailed design
We will change the grammar of `using_alias_directive` thusly:
```
using_alias_directive
- : 'using' identifier '=' namespace_or_type_name ';'
+ : 'using' identifier '=' (namespace_name | type) ';'
;
```
Interestingly, most of the spec language in [using_alias_directive](https://github.com/dotnet/csharplang/blob/main/spec/namespaces.md#using-alias-directives) does not need to change. Most language in it already refers to 'namespace or type', for example:
> A using_alias_directive introduces an identifier that serves as an alias for a namespace or type within the immediately enclosing compilation unit or namespace body.
This remains true, just that the grammar now allows the 'type' to be any arbitrary type, not the limited set allowed for by `namespace_or_type_name` previously.
The sections that do need updating are:
```diff
- The order in which using_alias_directives are written has no significance, and resolution of the namespace_or_type_name referenced by a using_alias_directive is not affected by the using_alias_directive itself or by other using_directives in the immediately containing compilation unit or namespace body. In other words, the namespace_or_type_name of a using_alias_directive is resolved as if the immediately containing compilation unit or namespace body had no using_directives. A using_alias_directive may however be affected by extern_alias_directives in the immediately containing compilation unit or namespace body. In the example
+ The order in which using_alias_directives are written has no significance, and resolution of the `(namespace_name | type)` referenced by a using_alias_directive is not affected by the using_alias_directive itself or by other using_directives in the immediately containing compilation unit or namespace body. In other words, the `(namespace_name | type)` of a using_alias_directive is resolved as if the immediately containing compilation unit or namespace body had no using_directives. A using_alias_directive may however be affected by extern_alias_directives in the immediately containing compilation unit or namespace body. In the example
```
```diff
- The namespace_name referenced by a using_namespace_directive is resolved in the same way as the namespace_or_type_name referenced by a using_alias_directive. Thus, using_namespace_directives in the same compilation unit or namespace body do not affect each other and can be written in any order.
+ The namespace_name referenced by a using_namespace_directive is resolved in the same way as the namespace_or_type_name referenced by a using_alias_directive. Thus, using_namespace_directives in the same compilation unit or namespace body do not affect each other and can be written in any order.
```
## Design meeting open questions.
This section needs to be resolved in a design meeting.
The intent of this specification is to allow one to write something like:
```
using MyPointer = My*;
```
The spec is currently unclear if this would be ok or not. Technically, the `using_alias_directive` here is not in an `unsafe` context, so the `My*` could be considered an error. However, the spirit of this specification is that should be allowed, and only the *usages* of `MyPointer` would themselves have to either be in another `using_alias_directive` or in an `unsafe` context. Another way this could be formalized is that the `(namespace_name | type)` portion of a `using_alias_directive` would always be an `unsafe` context, but that wouldn't negate the fact that any place that alias was referenced would also need to be an `unsafe` context.
--
Similarly what should be done about `using NullablePerson = Person?; // Person is a reference type`? My intuition is that this is fine (though should only be legal if the *using* is in a `#nullable enable` section). The meaning of `NullablePerson` in all reference locations is `Person?` (even if that location is `#nullable disable`). However, depending on the nullability region where it is referenced you may or may not get nullable warnings around it.

View file

@ -0,0 +1,176 @@
Utf8 Strings Literals
===
## Summary
This proposal adds the ability to write UTF8 string literals in C# and have them automatically encoded into their `byte[]` representation.
## Motivation
UTF8 is the language of the web and its use is necessary in significant portions of the .NET stack. While much of data comes in the form of `byte[]` off the network stack there is still significant uses of constants in the code. For example networking stack has to commonly write constants like `"HTTP/1.0\r\n"`, `" AUTH"` or . `"Content-Length: "`.
Today there is no efficient syntax for doing this as C# represents all strings using UTF16 encoding. That means developers have to choose between the convenience of encoding at runtime which incurs overhead, including the time spent at startup actually performing the encoding operation (and allocations if targeting a type that doesn't actually require them), or manually translating the bytes and storing in a `byte[]`.
```c#
// Efficient but verbose and error prone
static ReadOnlySpan<byte> AuthWithTrailingSpace => new byte[] { 0x41, 0x55, 0x54, 0x48, 0x20 };
WriteBytes(AuthWithTrailingSpace);
// Incurs allocation and startup costs performing an encoding that could have been done at compile-time
static readonly byte[] s_authWithTrailingSpace = Encoding.UTF8.GetBytes("AUTH ");
WriteBytes(s_authWithTrailingSpace);
// Simplest / most convenient but terribly inefficient
WriteBytes(Encoding.UTF8.GetBytes("AUTH "));
```
This trade off is a pain point that comes up frequently for our partners in the runtime, ASP.NET and Azure. Often times it causes them to leave performance on the table because they don't want to go through the hassle of writing out the `byte[]` encoding by hand.
To fix this we will allow for UTF8 literals in the language and encode them into the UTF8 `byte[]` at compile time.
## Detailed design
The language will allow conversions between `string` constants and `byte` sequences where the text is converted into the equivalent UTF8 byte representation. Specifically the compiler will allow for implicit conversions from `string` constants to `byte[]`, `Span<byte>`, and `ReadOnlySpan<byte>`.
```c#
byte[] array = "hello"; // new byte[] { 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20 }
Span<byte> span = "dog"; // new byte[] { 0x64, 0x6f, 0x67 }
ReadOnlySpan<byte> span = "cat"; // new byte[] { 0x63, 0x61, 0x74 }
```
When the input text for the conversion is a malformed UTF16 string then the language will emit an error:
```c#
const string text = "hello \uD801\uD802";
byte[] bytes = text; // Error: the input string is not valid UTF16
```
The predominant usage of this feature is expected to be with literals but it will work with any `string` constant value.
```c#
const string data = "dog"
ReadOnlySpan<byte> span = data; // new byte[] { 0x64, 0x6f, 0x67 }
```
In the case of any constant operation on strings, such as `+`, the encoding to UTF8 will occur on the final `string` vs. happening for the individual parts and then concatenating the results. This ordering is important to consider because it can impact whether or not the conversion succeeds.
```c#
const string first = "\uD83D"; // high surrogate
const string second = "\uDE00"; // low surrogate
ReadOnlySpan<byte> span = first + second;
```
The two parts here are invalid on their own as they are incomplete portions of a surrogate pair. Individually there is no correct translation to UTF8 but together they form a complete surrogate pair that can be successfully translated to UTF8.
Once implemented string literals will have the same problem that other literals have in the language: what type they represent depends on how they are used. C# provides a literal suffix to disambiguate the meaning for other literals. For example developers can write `3.14f` to force the value to be a `float` or `1l` to force the value to be a `long`. Similarly the language will provide the `u8` suffix on string literals to force the type to be UTF8.
When the `u8` suffix is used the literal can still be converted to any of the allowed types: `byte[]`, `Span<byte>` or `ReadOnlySpan<byte>`. The natural type though will be `ReadOnlySpan<byte>`.
```c#
string s1 = "hello"u8; // Error
var s2 = "hello"u8; // Okay and type is ReadOnlySpan<byte>
Span<byte> s3 = "hello"u8; // Okay
byte[] s4 = "hello"u8; // Okay
```
While the inputs to these conversions are constants and the data is fully encoded at compile time, the conversion is **not** considered constant by the language. That is because arrays are not constant today. If the definition of `const` is expanded in the future to consider arrays then this conversion should also be considered. Practically though this means a UTF8 literal cannot be used as the default value of an optional parameter.
```c#
// Error: The argument is not constant
void Write(ReadOnlySpan<byte> message = "missing") { ... }
```
The language will lower the UTF8 encoded strings exactly as if the developer had typed the resulting `byte[]` literal in code. For example:
```c#
ReadOnlySpan<byte> span = "hello";
// Equivalent to
ReadOnlySpan<byte> span = new byte[] { 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20 };
```
That means all optimizations that apply to the `new byte[] { ... }` form will apply to utf8 literals as well. This means the call site will be allocation free as C# will optimize this be stored in the `.data` section of the PE file.
## Drawbacks
### Relying on core APIs
The compiler implementation will use `UTF8Encoding` for both invalid string detection as well as translation to `byte[]`. The exact APIs will possibly depend on which target framework the compiler is using. But `UTF8Encoding` will be the workhorse of the implementation.
Historically the compiler has avoided using runtime APIs for literal processing. That is because it takes control of how constants are processed away from the language and into the runtime. Concretely it means items like bug fixes can change constant encoding and mean that the outcome of C# compilation depends on which runtime the compiler is executing on.
This is not a hypothetical problem. Early versions of Roslyn used `double.Parse` to handle floating point constant parsing. That caused a number of problems. First it meant that some floating point values had different representations between the native compiler and Roslyn. Second as .NET core envolved and fixed long standing bugs in the `double.Parse` code it meant that the meaning of those constants changed in the language depending on what runtime the compiler executed on. As a result the compiler ended up writing it's own version of floating point parsing code and removing the dependency on `double.Parse`.
This scenario was discussed with the runtime team and we do not feel it has the same problems we've hit before. The UTF8 parsing is stable across runtimes and there are no known issues in this area that are areas for future compat concerns. If one does come up we can re-evaluate the strategy.
## Alternatives
### Target type only
The design could rely on target typing only and remove the `u8` suffix on `string` literals. In the majority of cases today the `string` literal is being assigned directly to a `ReadOnlySpan<byte>` hence it's unnecessary.
```c#
ReadOnlySpan<byte> span = "Hello World;
```
The `u8` suffix exists primarily to support two scenarios: `var` and overload resolution. For the latter consider the following use case:
```c#
void Write(ReadOnlySpan<byte> span) { ... }
void Write(string s) {
var bytes = Encoding.Utf8.GetBytes(s);
Write(bytes.AsSpan());
}
```
Given the implementation it is better to call `Write(ReadOnlySpan<byte>)` and the `u8` suffix makes this convenient: `Write("hello"u8)`. Lacking that developers need to resort to awkward casting `Write((ReadOnlySpan<byte>)"hello")`.
Still this is a convenience item, the feature can exist without it and it is non-breaking to add it at a later time.
### Wait for Utf8String type
While the .NET ecosystem is standardizing on `ReadOnlySpan<byte>` as the defacto Utf8 string type today it's possible the runtime will introduce an actual `Utf8String` type is the future.
We should evaluate our design here in the face of this possible change and reflect on whether we'd regret the decisions we've made. This should be weighed though against the realistic probability we'll introduce `Utf8String`, a probability which seems to decrease every day we find `ReadOnlySpan<byte>` as an acceptable alternative.
It seems unlikely that we would regret the target type conversion between string literals and `ReadOnlySpan<byte>`. The use of `ReadOnlySpan<byte>` as utf8 is embedded in our APIs now and hence there is still value in the conversion even if `Utf8String` comes along and is a "better" type. The language could simply prefer conversions to `Utf8String` over `ReadOnlySpan<byte>`.
It seems more likely that we'd regret the `u8` suffix pointing to `ReadOnlySpan<byte>` instead of `Utf8String`. It would be similar to how we regret that `stackalloc int[]` has a natural type of `int*` instead of `Span<int>`. This is not a deal breaker though, just an inconvenience.
## Unresolved questions
### Depth of the conversion
Will it also work anywhere that a byte[] could work? Consider:
```c#
static readonly ReadOnlyMemory<byte> s_data1 = "Data"u8;
static readonly ReadOnlyMemory<byte> s_data2 = "Data";
```
The first example likely should work because of the natural type that comes from `u8`.
The second example is hard to make work because it requires conversions in both directions. That is unless we add `ReadOnlyMemory<byte>` as one of the allowed conversion types.
### Overload resolution breaks
The following API would become ambiguous:
```c#
M("");
static void M1(char[] charArray) => ...;
static void M1(byte[] charArray) => ...;
```
What should we do to address this?
## Examples today
Examples of where runtime has manually encoded the UTF8 bytes today
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/StatusCodes.cs#L13-L78
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs#L581-L591
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpResponseStream.Windows.cs#L284
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs#L30
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs#L852
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs#L35-L42
Examples where we leave perf on the table
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeChannelBindingHandle.cs#L16-L17
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs#L37-L43
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs#L78
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpCommands.cs#L669-L687
## Design meetings
<!-- Link to design notes that affect this proposal, and describe in one sentence for each what changes they led to. -->

View file

@ -4392,7 +4392,7 @@ GroupJoin(orders, c => c.CustomerID, o => o.CustomerID,
(c, co) => new { c, co }).
Select(x => new { x, n = x.co.Count() }).
Where(y => y.n >= 10).
Select(y => new { y.x.c.Name, OrderCount = y.n)
Select(y => new { y.x.c.Name, OrderCount = y.n })
```
where `x` and `y` are compiler generated identifiers that are otherwise invisible and inaccessible.