Compare commits

..

1 commit

Author SHA1 Message Date
AlekseyTs a1281e11dd
Adjust dependency rules for types in "Binding base clauses" section. 2019-09-05 14:01:11 -07:00
260 changed files with 1528 additions and 27138 deletions

1
.github/CODEOWNERS vendored
View file

@ -1 +0,0 @@
* @dotnet/roslyn-compiler

View file

@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Propose a language idea or ask a question
url: https://github.com/dotnet/csharplang/discussions/new
about: Starting with discussion is the way to create a new proposal.

View file

@ -1,50 +0,0 @@
---
name: Create a language specification
about: For proposals that have been invited by a team member.
title: "[Proposal]: [FEATURE_NAME]"
---
<!--
Hello, and thanks for your interest in contributing to C#! If you haven't been invited by a team member to open an issue, please instead open a discussion marked [draft issue] at https://github.com/dotnet/csharplang/discussions/new and we'll try to give you feedback on how to get to an issue-ready proposal.
New language feature proposals should fully fill out this template. This should include a complete detailed design, which describes the syntax of the feature, what that syntax means, and how it affects current parts of the spec. Please make sure to point out specific spec sections that need to be updated for this feature.
-->
# FEATURE_NAME
* [x] Proposed
* [ ] Prototype: Not Started
* [ ] Implementation: Not Started
* [ ] Specification: Not Started
## Summary
[summary]: #summary
<!-- One paragraph explanation of the feature. -->
## Motivation
[motivation]: #motivation
<!-- Why are we doing this? What use cases does it support? What is the expected outcome? -->
## Detailed design
[design]: #detailed-design
<!-- This is the bulk of the proposal. Explain the design in enough detail for somebody familiar with the language to understand, and for somebody familiar with the compiler to implement, and include examples of how the feature is used. Please include syntax and desired semantics for the change, including linking to the relevant parts of the existing C# spec to describe the changes necessary to implement this feature. An initial proposal does not need to cover all cases, but it should have enough detail to enable a language team member to bring this proposal to design if they so choose. -->
## Drawbacks
[drawbacks]: #drawbacks
<!-- Why should we *not* do this? -->
## Alternatives
[alternatives]: #alternatives
<!-- What other designs have been considered? What is the impact of not doing this? -->
## Unresolved questions
[unresolved]: #unresolved-questions
<!-- What parts of the design are still undecided? -->
## Design meetings
<!-- Link to design notes that affect this proposal, and describe in one sentence for each what changes they led to. -->

View file

@ -1,6 +0,0 @@
# Code of Conduct
This project has adopted the code of conduct defined by the Contributor Covenant
to clarify expected behavior in our community.
For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).

View file

@ -6,13 +6,9 @@
[![Join the chat at https://gitter.im/dotnet/csharplang](https://badges.gitter.im/dotnet/csharplang.svg)](https://gitter.im/dotnet/csharplang?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
- [Dotnet Discord](https://aka.ms/dotnet-discord-csharp) - github.com/dotnet discord for discussing dotnet repositories (including csharplang).
- [Discord](https://aka.ms/csharp-discord) - Any discussion related to the C# language up to the application level.
[![Chat on Discord](https://discordapp.com/api/guilds/143867839282020352/widget.png)](https://aka.ms/dotnet-discord-csharp)
- [C# Discord](https://aka.ms/csharp-discord) - General C# discussion not limited to the dotnet repositories.
[![Chat on Discord](https://discordapp.com/api/guilds/102860784329052160/widget.png)](https://aka.ms/csharp-discord)
[![Join the chat at https://aka.ms/csharp-discord](https://img.shields.io/discord/102860784329052160.svg)](https://aka.ms/csharp-discord)
- IRC - Any discussion related to the C# language up to the application level.

View file

@ -1,44 +1,111 @@
Features Added in C# Language Versions
====================
# C# 10.0 - .NET 6 and Visual Studio 2022 version 17.0
# [C# 1.0](https://en.wikipedia.org/wiki/Microsoft_Visual_Studio#.NET_.282002.29) (Visual Studio.NET)
- [Record structs](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-10.0/record-structs.md) and `with` expressions on structs (`record struct Point(int X, int Y);`, `var newPoint = point with { X = 100 };`).
- [Global using directives](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-10.0/GlobalUsingDirective.md): `global using` directives avoid repeating the same `using` directives across many files in your program.
- [Improved definite assignment](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/improved-definite-assignment.md): definite assignment and nullability analysis better handle common patterns such as `dictionary?.TryGetValue(key, out value) == true`.
- [Constant interpolated strings](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/constant_interpolated_strings.md): interpolated strings composed of constants are themselves constants.
- [Extended property patterns](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/extended-property-patterns.md): property patterns allow accessing nested members (`if (e is MethodCallExpression { Method.Name: "MethodName" })`).
- [Sealed record ToString](https://github.com/dotnet/csharplang/issues/4174): a record can inherit a base record with a sealed `ToString`.
- [Incremental source generators](https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md): improve the source generation experience in large projects by breaking down the source generation pipeline and caching intermediate results.
- [Mixed deconstructions](https://github.com/dotnet/csharplang/issues/125): deconstruction-assignments and deconstruction-declarations can be blended together (`(existingLocal, var declaredLocal) = expression`).
- [Method-level AsyncMethodBuilder](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/async-method-builders.md): the AsyncMethodBuilder used to compile an `async` method can be overridden locally.
- [#line span directive](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/enhanced-line-directives.md): allow source generators like Razor fine-grained control of the line mapping with `#line` directives that specify the destination span (`#line (startLine, startChar) - (endLine, endChar) charOffset "fileName"`).
- [Lambda improvements](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md): attributes and return types are allowed on lambdas; lambdas and method groups have a natural delegate type (`var f = short () => 1;`).
- [Interpolated string handlers](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/improved-interpolated-strings.md): interpolated string handler types allow efficient formatting of interpolated strings in assignments and invocations.
- [File-scoped namespaces](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/file-scoped-namespaces.md): files with a single namespace don't need extra braces or indentation (`namespace X.Y.Z;`).
- [Parameterless struct constructors](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/parameterless-struct-constructors.md): support parameterless constructors and instance field initializers for struct types.
- [CallerArgumentExpression](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/caller-argument-expression.md): this attribute allows capturing the expressions passed to a method as strings.
- Classes
- Structs
- Interfaces
- Events
- Properties
- Delegates
- Expressions
- Statements
- Attributes
- Literals
# C# 9.0 - .NET 5 and Visual Studio 2019 version 16.8
- [Records](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/records.md) and `with` expressions: succinctly declare reference types with value semantics (`record Point(int X, int Y);`, `var newPoint = point with { X = 100 };`).
- [Init-only setters](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/init.md): init-only properties can be set during object creation (`int Property { get; init; }`).
- [Top-level statements](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/top-level-statements.md): the entry point logic of a program can be written without declaring an explicit type or `Main` method.
- [Pattern matching enhancements](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/patterns3.md): relational patterns (`is < 30`), combinator patterns (`is >= 0 and <= 100`, `case 3 or 4:`, `is not null`), parenthesized patterns (`is int and (< 0 or > 100)`), type patterns (`case Type:`).
- [Native sized integers](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/native-integers.md): the numeric types `nint` and `nuint` match the platform memory size.
- [Function pointers](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/function-pointers.md): enable high-performance code leveraging IL instructions `ldftn` and `calli` (`delegate* <int, void> local;`)
- [Suppress emitting `localsinit` flag](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/skip-localsinit.md): attributing a method with `[SkipLocalsInit]` will suppress emitting the `localsinit` flag to reduce cost of zero-initialization.
- [Target-typed new expressions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/target-typed-new.md): `Point p = new(42, 43);`.
- [Static anonymous functions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/static-anonymous-functions.md): ensure that anonymous functions don't capture `this` or local variables (`static () => { ... };`).
- [Target-typed conditional expressions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/target-typed-conditional-expression.md): conditional expressions which lack a natural type can be target-typed (`int? x = b ? 1 : null;`).
- [Covariant return types](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/covariant-returns.md): a method override on reference types can declare a more derived return type.
- [Lambda discard parameters](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/lambda-discard-parameters.md): multiple parameters `_` appearing in a lambda are allowed and are discards.
- [Attributes on local functions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/local-function-attributes.md).
- [Module initializers](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/module-initializers.md): a method attributed with `[ModuleInitializer]` will be executed before any other code in the assembly.
- [Extension `GetEnumerator`](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/extension-getenumerator.md): an extension `GetEnumerator` method can be used in a `foreach`.
- [Partial methods with returned values](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/extending-partial-methods.md): partial methods can have any accessibility, return a type other than `void` and use `out` parameters, but must be implemented.
- [Source Generators](https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/)
# [C# 1.2](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history#c-version-12)
# C# 8.0 - .NET Core 3.0 and Visual Studio 2019 version 16.3
- Dispose in foreach
- foreach over string specialization
# [C# 2](https://msdn.microsoft.com/en-us/library/7cz8t42e(v=vs.80).aspx) (VS 2005)
- Generics
- Partial types
- Anonymous methods
- Iterators
- Nullable types
- Getter/setter separate accessibility
- Method group conversions (delegates)
- Static classes
- Delegate inference
# [C# 3](https://msdn.microsoft.com/en-us/library/bb308966.aspx) (VS 2008)
- Implicitly typed local variables
- Object and collection initializers
- Auto-Implemented properties
- Anonymous types
- Extension methods
- Query expressions
- Lambda expression
- Expression trees
- Partial methods
# [C# 4](https://msdn.microsoft.com/en-us/magazine/ff796223.aspx) (VS 2010)
- Dynamic binding
- Named and optional arguments
- Co- and Contra-variance for generic delegates and interfaces
- Embedded interop types ("NoPIA")
# [C# 5](https://blogs.msdn.microsoft.com/mvpawardprogram/2012/03/26/an-introduction-to-new-features-in-c-5-0/) (VS 2012)
- Asynchronous methods
- Caller info attributes
# [C# 6](https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6) (VS 2015)
- [Draft Specification online](https://github.com/dotnet/csharplang/blob/master/spec/README.md)
- Compiler-as-a-service (Roslyn)
- Import of static type members into namespace
- Exception filters
- Await in catch/finally blocks
- Auto property initializers
- Default values for getter-only properties
- Expression-bodied members
- Null propagator (null-conditional operator, succinct null checking)
- String interpolation
- nameof operator
- Dictionary initializer
# [C# 7.0](https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/) (Visual Studio 2017)
- [Out variables](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/out-var.md)
- [Pattern matching](https://github.com/dotnet/csharplang/blob/master/proposals/patterns.md)
- [Tuples](https://github.com/dotnet/roslyn/blob/master/docs/features/tuples.md)
- [Deconstruction](https://github.com/dotnet/roslyn/blob/master/docs/features/deconstruction.md)
- [Discards](https://github.com/dotnet/roslyn/blob/master/docs/features/discards.md)
- [Local Functions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/local-functions.md)
- [Binary Literals](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/binary-literals.md)
- [Digit Separators](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/digit-separators.md)
- Ref returns and locals
- [Generalized async return types](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)
- More expression-bodied members
- [Throw expressions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/throw-expression.md)
# [C# 7.1](https://blogs.msdn.microsoft.com/dotnet/2017/10/31/welcome-to-c-7-1/) (Visual Studio 2017 version 15.3)
- [Async main](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/async-main.md)
- [Default expressions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/target-typed-default.md)
- [Reference assemblies](https://github.com/dotnet/roslyn/blob/master/docs/features/refout.md)
- [Inferred tuple element names](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/infer-tuple-names.md)
- [Pattern-matching with generics](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/generics-pattern-match.md)
# [C# 7.2](https://blogs.msdn.microsoft.com/dotnet/2017/11/15/welcome-to-c-7-2-and-span/) (Visual Studio 2017 version 15.5)
- [Span and ref-like types](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/span-safety.md)
- [In parameters and readonly references](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/readonly-ref.md)
- [Ref conditional](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/conditional-ref.md)
- [Non-trailing named arguments](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/non-trailing-named-arguments.md)
- [Private protected accessibility](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/private-protected.md)
- [Digit separator after base specifier](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/leading-separator.md)
# C# 7.3 (Visual Studio 2017 version 15.7)
- `System.Enum`, `System.Delegate` and [`unmanaged`](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/blittable.md) constraints.
- [Ref local re-assignment](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/ref-local-reassignment.md): Ref locals and ref parameters can now be reassigned with the ref assignment operator (`= ref`).
- [Stackalloc initializers](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/stackalloc-array-initializers.md): Stack-allocated arrays can now be initialized, e.g. `Span<int> x = stackalloc[] { 1, 2, 3 };`.
- [Indexing movable fixed buffers](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/indexing-movable-fixed-fields.md): Fixed buffers can be indexed into without first being pinned.
- [Custom `fixed` statement](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/pattern-based-fixed.md): Types that implement a suitable `GetPinnableReference` can be used in a `fixed` statement.
- [Improved overload candidates](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/improved-overload-candidates.md): Some overload resolution candidates can be ruled out early, thus reducing ambiguities.
- [Expression variables in initializers and queries](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/expression-variables-in-initializers.md): Expression variables like `out var` and pattern variables are allowed in field initializers, constructor initializers and LINQ queries.
- [Tuple comparison](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/tuple-equality.md): Tuples can now be compared with `==` and `!=`.
- [Attributes on backing fields](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/auto-prop-field-attrs.md): Allows `[field: …]` attributes on an auto-implemented property to target its backing field.
# C# 8.0 (Visual Studio 2019 version 16.3, .NET Core 3.0)
- [Nullable reference types](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-8.0/nullable-reference-types-specification.md): express nullability intent on reference types with `?`, `notnull` constraint and annotations attributes in APIs, the compiler will use those to try and detect possible `null` values being dereferenced or passed to unsuitable APIs.
- [Default interface members](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-8.0/default-interface-methods.md): interfaces can now have members with default implementations, as well as static/private/protected/internal members except for state (ie. no fields).
- [Recursive patterns](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-8.0/patterns.md): positional and property patterns allow testing deeper into an object, and switch expressions allow for testing multiple patterns and producing corresponding results in a compact fashion.
@ -53,121 +120,3 @@ Features Added in C# Language Versions
- [Alternative interpolated verbatim strings](https://github.com/dotnet/csharplang/issues/1630): `@$"..."` strings are recognized as interpolated verbatim strings just like `$@"..."`.
- [Obsolete on property accessors](https://github.com/dotnet/csharplang/issues/2152): property accessors can now be individually marked as obsolete.
- [Permit `t is null` on unconstrained type parameter](https://github.com/dotnet/csharplang/issues/1284)
# C# 7.3 - Visual Studio 2017 version 15.7
- `System.Enum`, `System.Delegate` and [`unmanaged`](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/blittable.md) constraints.
- [Ref local re-assignment](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/ref-local-reassignment.md): Ref locals and ref parameters can now be reassigned with the ref assignment operator (`= ref`).
- [Stackalloc initializers](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/stackalloc-array-initializers.md): Stack-allocated arrays can now be initialized, e.g. `Span<int> x = stackalloc[] { 1, 2, 3 };`.
- [Indexing movable fixed buffers](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/indexing-movable-fixed-fields.md): Fixed buffers can be indexed into without first being pinned.
- [Custom `fixed` statement](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/pattern-based-fixed.md): Types that implement a suitable `GetPinnableReference` can be used in a `fixed` statement.
- [Improved overload candidates](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/improved-overload-candidates.md): Some overload resolution candidates can be ruled out early, thus reducing ambiguities.
- [Expression variables in initializers and queries](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/expression-variables-in-initializers.md): Expression variables like `out var` and pattern variables are allowed in field initializers, constructor initializers and LINQ queries.
- [Tuple comparison](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/tuple-equality.md): Tuples can now be compared with `==` and `!=`.
- [Attributes on backing fields](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.3/auto-prop-field-attrs.md): Allows `[field: …]` attributes on an auto-implemented property to target its backing field.
# [C# 7.2](https://blogs.msdn.microsoft.com/dotnet/2017/11/15/welcome-to-c-7-2-and-span/) - Visual Studio 2017 version 15.5
- [Span and ref-like types](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/span-safety.md)
- [In parameters and readonly references](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/readonly-ref.md)
- [Ref conditional](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/conditional-ref.md)
- [Non-trailing named arguments](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/non-trailing-named-arguments.md)
- [Private protected accessibility](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/private-protected.md)
- [Digit separator after base specifier](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/leading-separator.md)
# [C# 7.1](https://blogs.msdn.microsoft.com/dotnet/2017/10/31/welcome-to-c-7-1/) - Visual Studio 2017 version 15.3
- [Async main](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/async-main.md)
- [Default expressions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/target-typed-default.md)
- [Reference assemblies](https://github.com/dotnet/roslyn/blob/master/docs/features/refout.md)
- [Inferred tuple element names](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/infer-tuple-names.md)
- [Pattern-matching with generics](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/generics-pattern-match.md)
# [C# 7.0](https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/) - Visual Studio 2017
- [Out variables](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/out-var.md)
- [Pattern matching](https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.0/pattern-matching.md)
- [Tuples](https://github.com/dotnet/roslyn/blob/master/docs/features/tuples.md)
- [Deconstruction](https://github.com/dotnet/roslyn/blob/master/docs/features/deconstruction.md)
- [Discards](https://github.com/dotnet/roslyn/blob/master/docs/features/discards.md)
- [Local Functions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/local-functions.md)
- [Binary Literals](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/binary-literals.md)
- [Digit Separators](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/digit-separators.md)
- [Ref returns and locals](https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/ref-returns)
- [Generalized async return types](https://github.com/dotnet/roslyn/blob/master/docs/features/task-types.md)
- [More expression-bodied members](https://docs.microsoft.com/dotnet/csharp/programming-guide/statements-expressions-operators/expression-bodied-members)
- [Throw expressions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/throw-expression.md)
# [C# 6](https://github.com/dotnet/roslyn/blob/master/docs/wiki/New-Language-Features-in-C%23-6.md) - Visual Studio 2015
- [Draft Specification online](https://github.com/dotnet/csharplang/blob/master/spec/README.md)
- Compiler-as-a-service (Roslyn)
- [Import of static type members into namespace](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/using-static)
- [Exception filters](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/when)
- Await in catch/finally blocks
- Auto property initializers
- Default values for getter-only properties
- [Expression-bodied members](https://docs.microsoft.com/dotnet/csharp/programming-guide/statements-expressions-operators/expression-bodied-members)
- Null propagator (null-conditional operator, succinct null checking)
- [String interpolation](https://docs.microsoft.com/dotnet/csharp/language-reference/tokens/interpolated)
- [nameof operator](https://docs.microsoft.com/dotnet/csharp/language-reference/operators/nameof)
- Dictionary initializer
# [C# 5](https://blogs.msdn.microsoft.com/mvpawardprogram/2012/03/26/an-introduction-to-new-features-in-c-5-0/) - Visual Studio 2012
- [Asynchronous methods](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/async/)
- [Caller info attributes](https://docs.microsoft.com/dotnet/csharp/language-reference/attributes/caller-information)
- foreach loop was changed to generates a new loop variable rather than closing over the same variable every time
# [C# 4](https://msdn.microsoft.com/magazine/ff796223.aspx) - Visual Studio 2010
- [Dynamic binding](https://docs.microsoft.com/dotnet/csharp/programming-guide/types/using-type-dynamic)
- [Named and optional arguments](https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments)
- [Co- and Contra-variance for generic delegates and interfaces](https://docs.microsoft.com/dotnet/standard/generics/covariance-and-contravariance)
- [Embedded interop types ("NoPIA")](https://docs.microsoft.com/dotnet/framework/interop/type-equivalence-and-embedded-interop-types)
# [C# 3](https://msdn.microsoft.com/library/bb308966.aspx) - Visual Studio 2008
- [Implicitly typed local variables](https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/implicitly-typed-local-variables)
- [Object and collection initializers](https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers)
- [Auto-Implemented properties](https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/auto-implemented-properties)
- [Anonymous types](https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/anonymous-types)
- [Extension methods](https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/extension-methods)
- [Query expressions, a.k.a LINQ (Language Integrated Query)](https://docs.microsoft.com/dotnet/csharp/linq/query-expression-basics)
- [Lambda expression](https://docs.microsoft.com/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions)
- [Expression trees](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/expression-trees/)
- [Partial methods](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/partial-method)
- [Lock statement](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/lock-statement)
# [C# 2](https://msdn.microsoft.com/library/7cz8t42e(v=vs.80).aspx) - Visual Studio 2005
- [Generics](https://docs.microsoft.com/dotnet/csharp/programming-guide/generics/)
- [Partial types](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/partial-type)
- [Anonymous methods](https://docs.microsoft.com/dotnet/csharp/programming-guide/statements-expressions-operators/anonymous-functions)
- [Iterators, a.k.a yield statement](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/yield)
- [Nullable types](https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/nullable-value-types)
- Getter/setter separate accessibility
- Method group conversions (delegates)
- [Static classes](https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members)
- Delegate inference
- Type and namespace aliases
- [Covariance and contravariance](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/covariance-contravariance/)
# [C# 1.2](https://docs.microsoft.com/dotnet/csharp/whats-new/csharp-version-history#c-version-12) - Visual Studio .NET 2003
- Dispose in foreach
- foreach over string specialization
# [C# 1.0](https://en.wikipedia.org/wiki/Microsoft_Visual_Studio#.NET_.282002.29) - Visual Studio .NET 2002
- [Classes](https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/classes)
- [Structs](https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/struct)
- [Enums](https://docs.microsoft.com/dotnet/csharp/language-reference/builtin-types/enum)
- [Interfaces](https://docs.microsoft.com/dotnet/csharp/programming-guide/interfaces/)
- [Events](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/event)
- [Operator overloading](https://docs.microsoft.com/dotnet/csharp/language-reference/operators/operator-overloading)
- [User-defined conversion operators](https://docs.microsoft.com/dotnet/csharp/language-reference/operators/user-defined-conversion-operators)
- [Properties](https://docs.microsoft.com/dotnet/csharp/programming-guide/classes-and-structs/properties)
- [Indexers](https://docs.microsoft.com/dotnet/csharp/programming-guide/indexers/)
- Output parameters ([out](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/out) and [ref](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/ref))
- [`params` arrays](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/params)
- [Delegates](https://docs.microsoft.com/dotnet/csharp/programming-guide/delegates/)
- Expressions
- [using statement](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/using-statement)
- [goto statement](https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/goto)
- [Preprocessor directives](https://docs.microsoft.com/dotnet/csharp/language-reference/preprocessor-directives/)
- [Unsafe code and pointers](https://docs.microsoft.com/dotnet/csharp/programming-guide/unsafe-code-pointers/)
- [Attributes](https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/attributes/)
- Literals
- [Verbatim identifier](https://docs.microsoft.com/dotnet/csharp/language-reference/tokens/verbatim)
- Unsigned integer types
- [Boxing and unboxing](https://docs.microsoft.com/dotnet/csharp/programming-guide/types/boxing-and-unboxing)

View file

@ -1,6 +1,6 @@
# C# Language Design
[![Join the chat at https://gitter.im/dotnet/csharplang](https://badges.gitter.im/dotnet/csharplang.svg)](https://gitter.im/dotnet/csharplang?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Chat on Discord](https://discordapp.com/api/guilds/143867839282020352/widget.png)](https://aka.ms/dotnet-discord-csharp)
[![Join the chat at https://gitter.im/dotnet/csharplang](https://badges.gitter.im/dotnet/csharplang.svg)](https://gitter.im/dotnet/csharplang?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Welcome to the official repo for C# language design. This is where new C# language features are developed, adopted and specified.
@ -17,29 +17,23 @@ If you discover bugs or deficiencies in the above, please leave an issue to rais
For *new feature proposals*, however, please raise them for [discussion](https://github.com/dotnet/csharplang/labels/Discussion), and *only* submit a proposal as a pull request if invited to do so by a member of the Language Design Team (a "champion").
## Discussions
## Discussion
Debate pertaining to language features takes place in the form of [Discussions](https://github.com/dotnet/csharplang/discussions) in this repo.
Discussion pertaining to language features takes place in the form of issues in this repo, under the [Discussion label](https://github.com/dotnet/csharplang/labels/Discussion).
If you want to suggest a feature, discuss current design notes or proposals, etc., please [open a new Discussion topic](https://github.com/dotnet/csharplang/discussions/new).
If you want to suggest a feature, discuss current design notes or proposals, etc., please [open a new issue](https://github.com/dotnet/csharplang/issues/new), and it will be tagged Discussion.
Discussions that are short and stay on topic are much more likely to be read. If you leave comment number fifty, chances are that only a few people will read it. To make discussions easier to navigate and benefit from, please observe a few rules of thumb:
GitHub is not ideal for discussions, but it is beneficial to have language features discussed nearby to where the design artifacts are. Comment threads that are short and stay on topic are much more likely to be read. If you leave comment number fifty, chances are that only a few people will read it. To make discussions easier to navigate and benefit from, please observe a few rules of thumb:
- Discussion should be relevant to C# language design. If they are not, they will be summarily closed.
- Choose a descriptive topic that clearly communicates the scope of discussion.
- Stick to the topic of the discussion. If a comment is tangential, or goes into detail on a subtopic, start a new discussion and link back.
- Discussion should be relevant to C# language design. Issues that are not will be summarily closed.
- Choose a descriptive title for the issue, that clearly communicates the scope of discussion.
- Stick to the topic of the issue title. If a comment is tangential, start a new issue and link back.
- If a comment goes into detail on a subtopic, also consider starting a new issue and linking back.
- Is your comment useful for others to read, or can it be adequately expressed with an emoji reaction to an existing comment?
Language proposals which prevent specific syntax from occurring can be achieved with [a Roslyn analyzer](https://docs.microsoft.com/en-us/visualstudio/extensibility/getting-started-with-roslyn-analyzers). Proposals that only make existing syntax optionally illegal will be rejected by the language design committee to prevent increased language complexity.
## Proposals
Once you have a fully fleshed out proposal describing a new language feature in syntactic and semantic detail, please [open an issue for it](https://github.com/dotnet/csharplang/issues/new/choose), and it will be labeled as a [Proposal](https://github.com/dotnet/csharplang/issues?q=is%3Aopen+is%3Aissue+label%3AProposal). The comment thread on the issue can be used to hash out or briefly discuss details of the proposal, as well as pros and cons of adopting it into C#. If an issue does not meet the bar of being a full proposal, we may move it to a discussion, so that it can be "baked" further. Specific open issues or more expansive discussion with a proposal will often warrant opening a side discussion rather than cluttering the comment section on the issue.
When a member of the C# LDM finds that a proposal merits discussion, they can [Champion](https://github.com/dotnet/csharplang/issues?q=is%3Aopen+is%3Aissue+label%3A%22Proposal+champion%22) it, which means that they will bring it to the C# Language Design Meeting. If the LDM decides to work on adopting the feature, the proposer, the champion and others can collaborate on adding it as a document to the [Proposals](proposals) folder, where its evolution can be tracked over time.
## Design Process
[Proposals](proposals) evolve as a result of decisions in [Language Design Meetings](meetings), which are informed by [discussions](https://github.com/dotnet/csharplang/discussions), experiments, and offline design work.
[Proposals](proposals) are raised by, or on invitation from, "champions" on the LDT. They evolve as a result of decisions in [Language Design Meetings](meetings), which are informed by [discussion](https://github.com/dotnet/csharplang/labels/Discussion), experiments, and offline design work.
In many cases it will be necessary to implement and share a prototype of a feature in order to land on the right design, and ultimately decide whether to adopt the feature. Prototypes help discover both implementation and usability issues of a feature. A prototype should be implemented in a fork of the [Roslyn repo](https://github.com/dotnet/roslyn) and meet the following bar:
@ -51,15 +45,6 @@ Once approved, a feature should be fully implemented in [Roslyn](https://github.
**DISCLAIMER**: An active proposal is under active consideration for inclusion into a future version of the C# programming language but is not in any way guaranteed to ultimately be included in the next or any version of the language. A proposal may be postponed or rejected at any time during any phase of the above process based on feedback from the design team, community, code reviewers, or testing.
### Milestones
We have a few different milestones for issues on the repo:
* [Working Set](https://github.com/dotnet/csharplang/milestone/19) is the set of championed proposals that are currently being actively worked on. Not everything in this milestone will make the next version of C#, but it will get design time during the upcoming release.
* [Backlog](https://github.com/dotnet/csharplang/milestone/10) is the set of championed proposals that have been triaged, but are not being actively worked on. While discussion and ideas from the community are welcomed on these proposals, the cost of the design work and implementation review on these features are too high for us to consider community implementation until we are ready for it.
* [Any Time](https://github.com/dotnet/csharplang/milestone/14) is the set of championed proposals that have been triaged, but are not being actively worked on and are open to community implementation. Issues in this can be in one of 2 states: needs approved specification, and needs implementation. Those that need a specification still need to be presented during LDM for approval of the spec, but we are willing to take the time to do so at our earliest convenience.
* [Likely Never](https://github.com/dotnet/csharplang/milestone/13) is the set of proposals that the LDM has rejected from the language. Without strong need or community feedback, these proposals will not be considered in the future.
* Numbered milestones are the set of features that have been implemented for that particular language version. For closed milestones, these are the set of things that shipped with that release. For open milestones, features can be potentially pulled later if we discover compatability or other issues as we near release.
## Language Design Meetings
Language Design Meetings (LDMs) are held by the LDT and occasional invited guests, and are documented in Design Meeting Notes in the [meetings](meetings) folder, organized in folders by year. The lifetime of a design meeting note is described in [meetings/README.md](meetings/README.md). LDMs are where decisions about future C# versions are made, including which proposals to work on, how to evolve the proposals, and whether and when to adopt them.

View file

@ -274,7 +274,7 @@ A value-semantics class like the above would be automatically generated by a "re
class Point(int X, int Y);
```
By default, this would generate all of the above, except parameter names would be upper case. If you want to supersede default behavior, you can give it a body and do that explicitly. For instance, you could make X mutable:
By default, this would generate all of the above, except parameter names would be upper case. If you want to supercede default behavior, you can give it a body and do that explicitly. For instance, you could make X mutable:
``` c#
class Point(int X, int Y)

View file

@ -112,7 +112,7 @@ We should allow expression variables in queries, but keep them scoped to the ind
[csharplang/issues/287](https://github.com/dotnet/csharplang/issues/287)
The [proposal](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-10.0/caller-argument-expression.md) calls for an extra parameter with a default value (which is then replaced by the expression passed as an argument for the parameter designated in the attribute). This means that in a tie breaker situation, existing methods would match better than new ones that differ only by having this extra argument.
The [proposal](https://github.com/dotnet/csharplang/blob/master/proposals/caller-argument-expression.md) calls for an extra parameter with a default value (which is then replaced by the expression passed as an argument for the parameter designated in the attribute). This means that in a tie breaker situation, existing methods would match better than new ones that differ only by having this extra argument.
There are solutions to that for API owners:

View file

@ -13,7 +13,7 @@
The way `base.` works in classes, if the base implementation that was present at compile
time is removed at rune time, the CLR will search for the next implementation in the
hierarchy and use that instead. For example,
heirarchy and use that instead. For example,
```C#
class A

View file

@ -32,8 +32,8 @@ Internally to the member, the preconditions may affect the initial null-state of
These apply to anything that yields output: out parameters, return values, fields, properties, indexers, etc:
- `[MaybeNull]`:The output may be null, even if the type disallows it
- `[NotNull]`:For outputs (`ref`/`out` parameters, return values), the output will not be null, even if the type allows it. For inputs (by-value/`in` parameters) the value passed is known not to be null when we return.
- `[MaybeNull]`:The output may be null, even if the type disallows it
- `[NotNull]`:The output will not be null, even if the type allows it
On invocation, postconditions are applied after generic substitution, and affect the null state of the output. `[MaybeNull]` changes the null state of the output to “may be null”, whereas `[NotNull]` changes it to “not null”.

View file

@ -1,102 +0,0 @@
# C# Language Design Notes for Aug 26, 2019
## Agenda
Triage of newly championed issues. Features that we may consider are placed in the milestone where we will look at them again. Listed here by target milestone.
# Milestone 8.X
We are unsure if there will be an 8.1 release or we will go straight to C# 9.0. In the latter case we will triage this bucket and move things to 9.0 or later.
## #883 Zero and one-element tuples
We don't have a good syntax for one-tuples.
But in deconstruction and pattern matching there is less of a syntax problem.
What does `(3)` mean? It is a constant 3. If you want a tuple literal, you can just give it a name - that is unambiguous. `(_: 3)` might become the common pattern, though `_` isn't a discard for tuple element names.
Could consider splitting the feature and doing only one-element tuples in literals and deconstruction.
# Milestone 9.0
This is the next major release (as C# 8.0 is a done deal at this point). Putting features in this milestone doesn't mean that we will do them here! It just means that we will consider them in this timeframe - possibly to be rejected or pushed out at that point.
## #146
There's something to this, but instead of marking separately, we think it is paired with allowing nullary constructors on structs . For those we would warn on uses of `default(S)` that we can detect, similar to nullability.
## #812
Could work with conjunctive patterns. An alternative would be a range-like syntax.
## #1792, #1793, #1502
Let's keep ironing out annoying limitations in this space
## #1881
Should think also of UTF8
For `Memory<char>` etc we would probably continue to require you to go through `Span<char>` etc.
## #2585
Fits well with a "math" push, and should align with that.
# Milestone X.0
These would be major features, and we don't expect to consider them for C# 9.0.
## #339
This is a very fundamental feature, and we're not sure we have compelling scenarios. Possibly the next thing to look at after "type classes" in the advanced type features category.
## #1047
This is probably in pattern matching's future somewhere, though we won't be ready for it in 9.0.
## #538
Probably requires runtime work, and the scenarios don't currently justify that.
## #2545 (Blocked)
We keep not moving on this. We'd need a good understanding with EF
# Milestone X.X
These would be minor features which we don't expect to consider until after 9.0.
## #2608
Fine, but doesn't rise to priority
# Rejected
## #301
This is subsumed by "extension everything" and the other proposals in that ilk. We do understand and support the scenario, but don't want the specific feature.
## #1033
Rejected. The assignment should happen explicitly in the body, and the compiler can warn if you forget.
## #1586
It shipped and it's in unsafe. We're ok with it.
## #2383
The language is what it is at this point, and it is not worth doing work to "fix" it. However we should add a warning wave warning on the initializers saying they won't ever be called.

View file

@ -1,163 +0,0 @@
# C# Language Design Notes for Aug 28, 2019
## Agenda
1. Triage
## Discussion
### Hiding with optional parameters
If you define a method with optional parameters with the same name as a method
in a base class, we do not provide a warning that the method is hiding the method
in the base class, and if you put the `new` keyword, that provides a warning. Do
we want to provide a warning in a warning wave that the method is hiding and some
mechanism of silencing the warning (presumably allowing `new`).
**Conclusion**
If warning waves are here, we're comfortable taking this.
### `params Span<T>` and string formatting
There are a lot of important performance implications here. We think it should be
done soon, for the latest major release.
**Conclusion**
Agreed, next major release. (C# 9.0)
### Allow `sizeof` in safe code
There have been requests for this over the years, but we're not sure of the
actual value of the feature.
**Conclusion**
Rejected, until we hear compelling use cases.
### `Tail return` recursive calls
We could do "immediate" recursion (directly calling the containing method) without
depending on the runtime because we could implement it using compiler rewrites, but
it does seem like we would want to support mutual recursion or even just tail-dispatch.
This would require us to either accept the limitations in the CLR where a `tail` is
not always done in constant space, or require a CLR feature that guarantees constant space
However, we've done without tail calls for a quite a long time and there are some workarounds.
**Conclusion**
Let's talk about this later, for a C# 10.0.
### Variant method overrides
We previously pushed out overrides with variance (covariant returns, contravariant parameters) due
to requiring quadratic stubs to be emitted for the runtime. Now we can explore runtime changes to
support variance directly, where the runtime would allow overrides to differ with "compatible"
signatures, instead of exact matches, which is what is required right now.
**Conclusion**
There's a lot of existing use cases for this feature, and there's some feeling that it may be
required for the records feature. Discussion with the runtime says that it would not be
very expensive, and is tractable for the next major release. We're scheduling this for C# 9.0.
### Exponentiation operator (`**`)
This seems to be the major missing mathematical operator in C#. We like it as part of a broader
improvement to mathematical generalization that we're planning in the "shapes" proposals.
**Conclusion**
C# 10.0, matched up with shapes.
### `base(T)` with runtime changes
This is a follow-up feature for C# 8.0, so we'd like to do it as quickly possible.
**Conclusion**
Next major release, C# 9.0.
### Switching on `ReadOnlySpan<char>` with constant strings
We aren't ready to commit resources to it, since we haven't found blocking issues, but we'd
take the improvement whenever it's ready.
**Conclusion**
Any time.
### Permit a fixed field to be declared inside a readonly struct
We'd like to address performance issues soon if they're impactful enough.
**Conclusion**
Keep it in C# 9.0, pending confirmation of performance impact.
### Safe fixed-size buffers
This one has some compelling perf scenarios, but we don't have a clear design nailed down.
**Conclusion**
We'd like to take in a major release, but we need a solid design first.
### Comparison, `and`/`or`/`not` patterns
These seem useful and they are most useful with the `and`/`or` patterns.
**Conclusion**
This doesn't seem very big and we think it could fit in any release. C# 9.0 for now. Same
for `and`/`or` patterns.
### Dictionary Literals
Initialization of collection data structures, including immutable data structures, is a
known issue, in both performance and ergonomics. This proposal doesn't really address
these problems and special cases Dictionary<K,V>, even though we know the problem also
exists for other types.
**Conclusion**
The proposal in its current form is rejected. We'd rather try to address more problems
in a single feature, potentially after the records feature, which has proposals around
initialization of immutable types.
### No-arg constructor/non-defaultable value types
We're not very excited about it, but there are valid use cases. We previously pulled
the feature because there was a runtime bug that did not call the constructor in certain
cases. That bug has now been fixed.
**Conclusion**
### Target-typed conditional expression
We know it's useful and we have a design that we think would work.
**Conclusion**
We would like to do this for C# 9.0
### Surrogate pairs in Unicode-escaped code points in identifiers
The language spec already says this is allowed.
**Conclusion**
If we have a viable implementation, we'll take it whenever.
### Permit conditional operation with `int?` and double operands
This is separate from target-typing because it would change the common type algorithm,
but it also interacts with the target-typing, so we probably need to do them together.
**Conclusion**
Discuss before C# 9.0

View file

@ -1,101 +0,0 @@
# C# Language Design Meeting for September 4, 2019
## Agenda
1. `[AllowNull]` on properties
## Discussion
[https://github.com/dotnet/roslyn/issues/37313](https://github.com/dotnet/roslyn/issues/37313)
### AllowNull and flow analysis
Some questions have come up concerning `AllowNull` and property analysis, like in the following example:
```C#
class Program
{
private string _f = string.Empty;
[AllowNull]
public string P
{
get => _f;
set => _f = value ?? string.Empty;
}
static void Main(string[] args)
{
var p = new Program();
p.P = null; // No warning, as expected
Console.Write(p.P.Length); // unexpected warning
}
}
```
The tricky part here is that we are tracking flow state for properties, meaning if `null` is stored in a
property, we treat that property as having a null state. Although there is an attribute, the flow state
of the variable wins, meaning that we think there is a `null` inside `P`, even though the stated type
is `string`. Similarly, if you invert the attribute and use `[NotNull]` on a `string?`, the flow state
will win again.
There a couple ideas to address the problem, including
1. Don't flow-track non-nullable fields/properties.
2. Have `[AllowNull]` suppress both the warning, and suppress setting the flow state to `null`
3. Have the `[NotNull]` attribute suppress the flow state (which it currently doesn't), and require
the property be written with a nullable type (`string?`) and have `[NotNull]` on the getter.
4. Stop tracking when any nullability attributes are present on properties.
2 and 3 are somewhat related and we could do both, in theory. The main problem is that it greatly
complicates the user's understanding of when flow tracking is enabled, and there are also potentially
downstream affects for type inference if we allow the rule for (2) to apply to parameters.
For (1) it seems plausible, since we would still have a warning on assigning a null to a non-nullable
member. This would remove the flow-based subsequent warnings, but the user would still be warned at
the point where the problem happens. However, it doesn't seem to solve the problem for generics, e.g.
```C#
class C<T> where T : new()
{
private T _f = new T();
[AllowNull]
public T P
{
get => _f ?? throw new Exception();
set
{
if (value is null)
{
throw new Exception();
}
return _f;
}
}
}
```
Here the generic property `P` cannot be marked nullable because it is unconstrained.
However, 2 & 3 also have a problem around `MaybeNull`. If a property is annotated `MaybeNull`, then
the attribute would override the state, meaning that a null check is useless. If you check for null
it would not matter, because when you read the property again, the attribute would override the state
and the result would still be maybe null.
An idea to address this is a combination of (4) and (3), where we have special attribute behavior
for properties, and in that case `NotNull` has precedence over nullable state, but nullable state
has precedence over `MaybeNull`. In addition, `AllowNull` modifies the state transition to use
the declared state if the input is null, while it otherwise uses the non-null state.
**Conclusion**
The proposal starting point is:
NotNull wins over tracked state, which wins over MaybeNull.
AllowNull transforms an incoming maybe null state to the declared state.
There's an action item to go investigate how this will play into the rest nullability, and an open
question of whether to treat fields like properties, or like local variables.

View file

@ -1,301 +0,0 @@
# C# Language Design Meeting for Sep. 11, 2019
## Agenda
## Discussion
### Nullable attributes and flow state interaction
We started this discussion with an email with a proposal based on follow-up research from the
previous meeting.
### Attribute interaction proposal
> Allowed inputs and outputs:
>
> First of all, Ill try to make rules based only on “allowed inputs” and “allowed outputs” of a
> property. Ill use shorthands ?, ! and T for “nullable”, “nonnullable” and “unknown depends on
> T” respectively.
>
> For all the different sensible combinations of attributes, here are the “allowed inputs” and “allowed outputs”:
> | | Allowed input | Allowed output |
> |----------------------------|---------------|----------------|
> | string | ! | ! |
> | [AllowNull]string | ? | ! |
> | [NotNull]string? | ? | ! |
> | [MaybeNull]string | ! | ? |
> | [DisallowNull]string? | ! | ? |
> | string? | ? | ? |
> | [DisallowNull][NotNull]T | ! | ! |
> | [NotNull]T | T | ! |
> | [AllowNull][NotNull]T | ? | ! |
> | [DisallowNull]T | ! | T |
> | T | T | T |
> | [AllowNull]T | ? | T |
> | [DisallowNull][MaybeNull]T | ! | ? |
> | [MaybeNull]T | T | ? |
> | [AllowNull][MaybeNull]T | ? | ? |
> There should be no surprises to anyone there. Now lets use these to define the different behaviors around properties:
> Ordering of states: T is stricter than ? and ! is stricter than both T and ?.
> This is a measure of relative permissiveness of states.
> Initial state: The initial state of a property is its allowed output.
> This corresponds to us knowing nothing about the property yet, beyond what it tells us through a combination of its type and its postconditions.
> State after null check:
>
> - On the non-null branch of a null check the state of the property is !.
> - On the null branch of a pure null check the state of the property is ?.
> - Elsewhere the state of the property is unchanged.
>
> These rules reflect the general benefit of a null check, as well as the overriding effect of a pure null check even of a nonnull property.
Note that this rules out "dangerous" properties, meaning properties that may change outside the
scope of the nullable analysis, as in fields which may be changed by a different thread, and thus
the null check is unreliable. We don't consider this scenario to be in scope of our current
design and if we decide to address this, we must create a new attribute or some other mechanism.
There's also some problem with the state after null checks, namely that the type may not support
the `?` state. For instance, in the following unconstrained generic,
```C#
T M<T>(T t)
{
if (t != null)
t.ToString();
return t;
}
```
we should not produce a warning on the return, since the state should match the legal state of
`T`, which is `T`, not `?`. Similarly, for non-Nullable value types, the state cannot be `?`
after a null check, since the value cannot be null. The rule should use the `T` state.
> State after assignment:
>
> The state of the property after an assignment is
>
> - Its initial state if the state of the assigned value is at least as strict as the allowed input, but no stricter than the allowed output
> - The state of the assigned value otherwise
>
> This rule reflects that a property is expected to “take care of things” when the state of an assigned value is valid as input but not as output. It does so by assuming that the resulting state in such situations is something thats valid as output.
>
> This is probably the only rule that would differ from the rules for fields, which would continue to always use the state of the assigned value.
> Warnings on assignment: A warning is yielded if the state of the assigned value is less strict than the allowed input.
> This is the same rule as all other input positions.
We like this new "state after assignment" rule and think it can be implemented now.
However, when looking into the solution, we found that this is the current behavior for properties when annotated:
```C#
using System;
using System.Diagnostics.CodeAnalysis;
#nullable enable
class C<T> where T : class?
{
public C(T x) => f = x;
T f;
T P1 { get => f; set => f = value; }
[AllowNull] T P2 { get => f; set => f = value ?? throw new ArgumentNullException(); }
[MaybeNull] T P3 { get => default!; set => f = value; }
void M()
{
P1 = null; // Warning
P2 = null; // No warning
P3 = null; // Warning
f = P1; // No warning
f = P2; // No warning
f = P3; // BUG?: No warning!
}
}
```
That last line does not look right. Similar to `default(T)`, you could be producing a
potentially nullable value, when the substituted type may not permit it. We think
the three state domain outlined above will solve the problem, but that would
be too extensive to change to perform in such a short period of time.
Alternative: whenever you introduce a value (by calling
a property or a method), that produces a generic type annotated with `[MaybeNull]`
in a substituted generic method, that would produce a warning. This
matches our current behavior for `default(T)`.
For example,
```C#
T M<T>()
{
_ = (new List<T>).FirstOrDefault(); // this would now produce a warning
}
```
**Conclusion**
Let's implement the "state after assignment" rule as defined and implement the
"Alternative" proposal outlined above. We will consider updating to use the
"three state domain" above later, which may have some further changes. A sample
of the expected behavior follows:
Non-Generic
```C#
using System;
using System.Diagnostics.CodeAnalysis;
class Widget {
string _description = string.Empty;
[AllowNull]
string Description {
get => _description;
set => _description = value ?? string.Empty;
}
static void Test(Widget w) {
w.Description = null; // ok
Console.WriteLine(w.Description.ToUpper()); // ok
if (w.Description == null) {
Console.WriteLine(w.Description.ToUpper()); // warning
}
}
}
```
Generic
```C#
using System;
using System.Diagnostics.CodeAnalysis;
class Box<T> {
T _value;
[AllowNull]
T Value {
get => _value;
set {
if (value != null) {
_value = value;
}
}
}
static void TestConstrained<U>(Box<U> box) where U : class {
box.Value = null; // ok
Console.WriteLine(box.Value.ToString()); // ok
if (box.Value == null) {
Console.WriteLine(box.Value.ToString()); // warning
}
}
static void TestUnconstrained<U>(Box<U> box, U value) {
box.Value = default(U); // 'default(U)' always produces a warning when U could be a non-nullable reference type
Console.WriteLine(box.Value.ToString()); // ok
box.Value = value; // ok
Console.WriteLine(box.Value.ToString()); // ok
if (box.Value == null) {
Console.WriteLine(box.Value.ToString()); // warning
}
}
}
```
## More triage
### Top-level statements and member declarations
We have a variety of different use cases and experimental products (C# Interactive Window,
Jupyter projects, try.net, etc) that use the current "C# scripting" language, which is already
effectively a dialect of C#. There's a fair amount of concern that if adoption continues, we may produce a fracturing of the C# language.
However, adding top-level statements and reconciling scripting in C# proper would be an expensive
feature, in both design and implementation. It also doesn't directly impact many of the designs
we're currently considering.
But there is also significant cost to doing nothing. We have not considered the semantic for many
features in C# 8, or even if they should work in scripting (`using` declarations, notably). There
is a significant ongoing cost here, either in considering all our designs for the scripting
dialect, or in risk that not doing design/implementation work will cause bad experiences for
products using the C# scripting code.
**Conclusion**
We'll schedule this for 9.0, to at least examine options.
### Primary constructors
This occupies the same design space as records, which is scheduled for 9.0, so
we at least need to consider this feature while implementing records.
**Conclusion**
Moving to 9.0.
### Negated-condition if statement
Issue #882
This overlaps significantly with a "is not" pattern. We're not confident this
feature has significant value, after the "is not" pattern is implemented.
**Conclusion**
Move to X.X to consider after "is not" has shipped and see if there are significant
use cases that are not addressed by the "is not" pattern.
### Allow `default` in deconstruction
Issue #1394
The primary use case is `(x, y, z) = default;` instead of naming each variable
individually. There are some issues around the written specification, specifically
on what target typing `default` has.
**Conclusion**
From a consistency perspective it seems like this should work, regardless of the
complexity in details of the specification. We'll take this Any Time whenever we have a solid
specification and implementation.
### Partial type inference
Issue #1349
Nothing that's related to type inference is a tiny feature, but this is pretty
small as type inference changes are concerned. We think the hardest problem will
be agreeing on the syntax. Agreed that it could be useful, though.
**Conclusion**
We'll take this Any Time.
### Declaration expressions
Issue #973
Somewhat related is "sequence expressions". This is useful for declaring variables inline in an
expression. There are places where statements are not possible, and this requires
refactoring.
**Conclusion**
We don't think there's value in half measures here. We think going all the way to sequence
expressions may have value, but then declaration expressions do not.

View file

@ -1,121 +0,0 @@
# C# Language Design Meeting for September 16, 2019
## Agenda
1. UTF-8 strings (https://github.com/dotnet/corefxlab/issues/2350)
2. Triage
## Discussion
### UTF-8 Strings
Motivation: UTF-8 is everywhere, and converting to and from the .NET native string type (UCS2/UTF-16),
can be expensive and confusing.
#### Language impact
**Literals**
Current proposal is to emit the data as UTF-16, and use a runtime helper to project to UTF-8. The
main question for the language is if this encoding limits the usability in some way, and whether
it blocks us off from introducing an optimal strategy later. There are two things to consider: the
cost in the compiler, and the cost to the language. At the moment we don't see anything that would
be considered a breaking change in the language.
**Enumeration**
The proposal doesn't include any default enumeration. It's worth considering if this violates C#
user expectations. There are different forms of enumeration available by calling properties, but
none of them are the default. Should there be a default?
One problem is that users who see enumeration may expect indexing, which is not cheap, and does
not match expectations. Another problem is that there is almost always a better operation than
enumerating a UTF-8 string. It seems like adding this more likely to encourage a user to write a
bug, or inefficient code, than to help them.
**Why language support**
The main advantages of language support are:
* O(n) string concatenation (calling utf8string.Concat with all `n` arguments)
* String literals
**Target-typing**
Target-typing of existing string literals is possible but produces "bad" behavior for seemingly
the most common scenario:
```C#
void M(string s) { ... }
void M(Utf8String s) { ... }
M("some string"); // This would call the `string` version, because backwards compatibility requires
// "" always be a `string` first, and a `Utf8String` second
```
**Syntax**
As always, there's debate about the syntax. The "u" prefix is somewhat unsatisfying because UTF-8,
UTF-16, UCS-2, *and* `unsigned` all begin with "u". The same complaints hold for the proposed
`ustring` contextual keyword. However, there are no enthusiastically favored alternatives.
In addition, the value of the `ustring` contextual keyword seems questionable. If the brevity is
important, then it seems important enough that the framework could call it `ustring` or `utf8`.
One argument is that Utf8String could be a new "primitive" type, and we should add a keyword for
all primitive types. However, this is not proposed as a primitive type at the same level, so that
weakens support. In addition, the original aliases were all added at the inception of the language,
so we have no precedence for adding either primitive types or aliases.
**Conclusion**
We think the feature is valuable and probably worth some language support. We think a literal syntax
(like the "u" prefix) is good, but we don't like target typing. We're also not convinced of the need
for a contextual keyword.
### More triage
#### Null parameter checking
Issue #2145
ASAP, 9.0
### CallerArgumentExpression
Issue #287
We could potentially use this to implement "Null parameter checking" as a method call, instead of
new syntax. Thus, consider for 9.0.
### Relax ordering constraints about modifiers (especially `ref` and `partial`)
Issue #946
9.0
### Zero- and one-element tuples
Issue #883
In terms of language value, zero- and one-element tuples are different features. Zero-element
tuples are most useful as a unit type, which is not necessarily the unit type we would choose
to standardize on (e.g. against System.Void), while one-element tuples are simply useful as a
wrapper type and don't have an obvious competitor.
We need to revisit the decisions here. Moving to X.X
### Mix declarations and variable in deconstruction
Issue #125
Not an urgent feature, but useful for completeness. On the other hand, it's always very clear
whether or not the deconstruction is using existing variables, or creating new ones.
Any time
### Discard for lambda parameters
Issue #111
Any time

View file

@ -1,98 +0,0 @@
# C# Language Design Meeting for September 18th, 2019
## Agenda
Triage:
1. Proposals with complete designs:
- https://github.com/dotnet/csharplang/issues/1888 Champion "Permit attributes on local functions"
- https://github.com/dotnet/csharplang/issues/100 Champion "Target-typed new expression"
2. Target typing and best-common-type features:
- https://github.com/dotnet/csharplang/issues/2473 Proposal: Target typed null coalescing (??) expression
- https://github.com/dotnet/csharplang/issues/2460 Champion: target-typed conditional expression
- https://github.com/dotnet/csharplang/issues/881 Permit ternary operation with int? and double operands
- https://github.com/dotnet/csharplang/issues/33 Champion "Nullable-enhanced common type"
## Discussion
### Attributes on local functions
Issue #1888
The only major concern about the feature is whether or not the attributes are guaranteed to be emitted to
the lowered method, assuming there is a lowered method. For all current lowering strategies, there is a
specific place to put the attributes. The proposal is that we always emit attributes for our current
scenarios, but don't guarantee in the language that all attributes will survive rewriting.
There is one exception to the above, which is static local functions. Static local functions are specified
to always emit a static method with the same signature as the local function (although there are bugs about
this today). Attributes on static local function would have a similar guarantee, meaning that they are always
emitted to the lowered methods, on the exact same parameters they were applied to.
**Conclusion**
Proposal accepted, attributes will be allowed on local functions. `extern` will also be allowed on
static local functions, and will be a valid P/Invoke target.
### Target-typed new
Issue #100
We like the feature and already have a design. However, we want to do a quick review that we still like the
design, taking into account all the changes we've made to the language since it was proposed.
**Conclusion**
Accepted, pending review.
### Target-typed conditional expression and nullable-enhanced common type
Issue #2460 and issue #33
The main problem is that providing a target-typing for the switch expression
may have made issue #33 a breaking change. In general, additional target-typing
(on top of a natural type) works by calculating the target type if there is no
viable natural type. By allowing more natural types, it reduces the ability to
use target-typing, which may allow more resulting types than the natural type
inference.
Overload resolution is a good example. If you have two methods
```C#
void M(int? x) { ... }
void M(short? y) { ... }
```
and a call `M(e switch { ... => 1, ... => null })`, when we do overload resolution
we consider the target type for the `switch`, which consists of the target type
for each of the candidates. For target type, `short` is a better target for `int`.
If we improved the natural type, then the natural type of `1` would be `int`. Then,
if we improve the natural type of `e switch { ... => 1, ... => null}` to be `int?`,
then a different overload (`M(int? x)`) will be chosen.
There's also a pretty clear trade-off here. Target-typing may allow more viable
types, but it falls over in the presence of type inference or if the target is
so broad as to be useless (like if the target-type is `object`).
Additionally, the proposal for target-typing the conditional expression (`?:`) will have
different semantics from the target-typing for the switch expression, to avoid a breaking change.
It may be desirable to unify the behavior of things like `?:` and switch expression, even if that
means that the switch expression would be more limited, due to the backwards compatibility
constraints of `?:`.
**Conclusion**
Let's do some work to see if we can alter the interaction of target-typing and common type
inference to add the proposed new common types without introducing breaking changes with
target-typing. The hope is that such a rule could be general enough to apply to other potential
breaks in the future (in case we find more potential improvements to common type).
In addition, we would like to know the effect of changing switch expressions to prefer the common
type, if one exists. We hope that any difference would be uncommon in practice, but it would be
useful to know if there are common instances where this would break. This would be used to decide
if we would adjust the behavior of the switch expression to match the proposed behavior for the
conditional expression.

View file

@ -1,116 +0,0 @@
# C# Language Design Meeting for Oct. 21
## Agenda
1. Records
2. Init-only members
3. Static lambdas
## Discussion
We'd like to make some progress on records, in both design and implementation. One development in
our view of records is to consider them not as a feature in and of itself, but a shorthand for a
set of features aimed at working with a collection of data. Specifically, we have a proposal to
consider a guiding principle: a record should only be capable of generating code that the user
could write themselves.
To that end, we can look at the set of features we're considering incorporating into records, and
differentiate between them based on the uncertainty of their design. The features which may have
generated behavior are:
1. Automatic structural equality
2. With-ers (per type?)
3. Object initializers for readonly (per member)
4. Primary constructors + deconstruction
Some features, like with-ers and object initializers, have many open issues. Some
features, like generation of structural equality, have widely agreed upon semantics,
once the set of members that are part of the structure are decided.
It's proposed that we take a subset of the features, namely primary constructors and structural equality, and consider them to have designs ready for implementation. Primary constructors still have open semantic questions, especially around their meaning outside of a records, but all of the proposed records specify some type of primary constructor with very similar semantics. An example of the common semantics would be the following,
```C#
data class Person(string Name, DateTime Birthday);
```
which would generate two record members, `Name` and `Birthday`, structural equality on those two
members, and a corresponding constructor/deconstructor pair. The `data` modifier, as well as the
primary constructor, are not necessarily final syntax, but the semantic decisions downstream of
this stand-in don't heavily depend on the specific form of syntax chosen and could be easily
changed.
**Conclusion**
This looks like a reasonable starting point. Records and the components should
definitely be designed together, to ensure they fit together well, but it's worth
highlighting the more settled pieces as we go.
#### Init-only
A brief discussion of the init-only fields feature. One clarification: the
init-only properties would be allowed to have setters. Those setters can be
executed in the object initializer, or in the constructor of the object. There
is a proposal to allow them also to be set in the constructors of base types,
which has no opposition at the moment.
There's also a problem with "required" init-only members as currently proposed.
The current design specifies that "required" init-only members (members without
an initializer) would produce an error on the construction side if the member
is not initialized in an object initializer.
For example,
```C#
class Person
{
public initonly string Name { get; }
}
void M()
{
var person1 = new Person() { Name = "person1" }; // OK
var person2 = new Person(); // Error, Name was not initialized
}
```
Unfortunately, the proposed emit strategy for `initonly` members would only provide
an error in source, not in metadata. If a consumer compiled against a previous
implementation of a type, and a required `initonly` member was added, no error would
be provided if the consumer were not recompiled. Instead, the member would silently
be set to the default value.
A simple alternative is to drop "required" `initonly` members entirely. Setting an initonly
member would be optional, as it is for public readonly members today, and if it is not set it
would retain its default value. The recommendation for adding required members would be the same
as it is today: use a constructor parameter, which cannot be skipped.
We could also attempt to repair the situation by lowering the required `initonly`
members into constructor parameters, but this seems undesirable for many reasons,
including that it's complex to implement, it risks creating collisions with
constructor overloads with the same type, it creates a mismatch in the number of
parameters between source and IL, etc. It does not seem worth going down this path.
### Static lambdas
Static lambdas were elided mostly for time reasons and we're interested in bringing
them back. The main hang-up on the syntax is adding modifiers before the lambda
syntax, but we already crossed that bridge with `async`.
The only semantics question is whether or not a static lambda guarantees that the
emitted method will be static in metadata. This is true for static local functions,
but there are some important scenarios, like `extern` local functions, which depend
on that and could not be implemented for lambdas. In addition, there have been
numerous performance improvements in lambdas and local functions that have taken
advantage of their unspecified metadata characteristics, and at least one significant
performance optimization for lambdas would be lost by requiring them to be static.
**Conclusion**
The `static` keyword will be allowed on lambdas and it will have the same capturing rules as
static local functions. It will not require that the lambda be emitted as static.

View file

@ -1,92 +0,0 @@
# C# Language Design Meeting for Oct. 23, 2019
## Agenda
1. New primitive types (`nint` and `nuint`)
## Discussion
### Native int
Proposal: https://github.com/dotnet/csharplang/issues/435
#### Static members
The current spec states that `nint` and `nuint` are reserved keywords in a type context but there
are contexts, like qualified names, that allow types but are not type contexts. Since `nint` and
`nuint` have static members, we want users to be able to access them, so they should be allowed
in qualified names.
#### IntPtr members
A follow-up question is what to do about existing members on the underlying IntPtr
type. There are some members, like Add and Subtract, which may be misleading because
they are not equivalent to `+` and `-` due to over/underflow checking.
There are two views here. On the one hand, the proposal to use IntPtr means that
some implementation details for IntPtr will inevitably leak through. Since users
will sometimes want to use an IntPtr as an `nuint` to do pointer arithmetic, any
methods on IntPtr may be useful and this would just be making barriers.
On the other hand, `nint` is being added as a type for conceptually different reasons. It
shouldn't be assumed that everything that applies to IntPtr should be applicable to `nint`.
There's a follow-up question as whether we would exclude everything on IntPtr except for an
include list, or include everything except for an exclude list. These may seem very different,
but after more thought it's likely they're very similar. Since the framework would probably very
rarely add new members to IntPtr after the language change (like with System.Int32), what we pick
now is probably what will be present for a very long time, and anything new would probably be
done with consultation from the language team.
Our tentative conclusion is that we should exclude some members. The following
are questionable:
* Zero (use the `0` literal)
* Size (use sizeof)
* Add, Subtract (part of the point of this type is to use "proper" C# arithmetic)
* ToInt32, ToInt64, ToPointer (use the language-defined conversions)
#### Signed bitwise operations
Unfortunately, IntPtr is "signed" but is often used to represent pointers, which are unsigned
according to framework guidelines. In the current design, IntPtr has an identity conversion to
`nint`, which could cause the unfortunate scenario
```C#
IntPtr M(IntPtr p)
{
nint x = p;
x >> 1;
return x;
}
```
This would use signed `>>`, which is probably incorrect if the value is assumed to be an unsigned
pointer. This is not currently a problem with IntPtr because IntPtr does not define any bitwise
operators.
Unfortunately, keeping an identity conversion is an important part of compatibility. We would
like to support users who are currently using IntPtr, but would like to use `nint`, to go from
`IntPtr[]` to `nint[]`. Without an identity conversion, we don't see how this would be possible
in the language today.
Adding an entirely new concept in the language to support this without identity conversions is
probably not worth it. Unless we can find a simple improvement to the design, we will probably
have to accept this behavior.
#### Overriding, hiding, and implementation (OHI)
Since IntPtr and nint are the same CLR underlying type, they will be regarded as having the same
signature. The compiler is able to see the difference. Should we regard them as identical for the
purposes of OHI? This is similar to scenarios like tuple names, which are represented as
attributes and thus not visible as part of the CLR type. However, in OHI we provide an error if
tuple names change in an override because this could be the source of a bug (e.g., if the names
were accidentally swapped). In this case we think IntPtr and `nint` are similar enough that we
should regard the OHI behavior similar to `dynamic`/`object`, where it is allowed.
For use as an enum underlying type, we think `nint`/`nuint` should be allowed as long as it's not
too much implementation work. The legal values would be the same as the valid constants for the
underlying.
For sizeof, we think it should be supported in unsafe code, just like `sizeof(IntPtr)`, but with
no special behavior in safe code.

View file

@ -1,113 +0,0 @@
# C# Language Design Notes for Oct. 28, 2019
## Agenda
1. Discard parameters in lambdas and other methods
1. Enhancing the common type algorithm
## Discussion
### Discard parameters
We wanted to talk about discard parameters in lambdas, and also potential expansions into other
parameter lists like in local functions and regular methods.
The first hurdle is whether any form of this feature is worth the complexity. The simplest case
is lambdas, because the parameter names are not visible to the caller so they are only visible to
the implementation. The alternative to discards is to give throw-away names, like
`_1`, `_2`, and `_3`, or to use anonymous method syntax (`delegate { }`) to ignore
all parameters.
As for value, this is a fairly commonly requested requested feature ever since we introduced
discards. The cost is increased complexity in the language (understanding that discards are legal
as lambda parameters), but there's also an argument that not having the feature causes more
complexity in the language. Specifically, the anonymous method syntax is almost entirely obsolete
compared to the lambda syntax, but is still commonly used in this exact scenario. If usage of
this feature decreases the usage of anonymous method syntax, that could be a decrease in required
understanding of the language.
For discards in local functions and method parameter lists, the cost/value ratio is not nearly as
clear. The fundamental limitation is that parameter names for methods and local functions are
always public surface area to the caller. We find the cost in complexity in resolving these
questions higher than the feature is currently worth. If we find that it becomes a highly desired
feature later, we would reconsider this decision.
Lastly, we had a question of how the scoping would work regarding `_` in both
the enclosing scope and inner scope of lambdas.
```C#
void M()
{
int _ = 0;
Action<int, int> a = (_, _) =>
{
_ = 1; // Is this a discard, or does it capture the local above?
};
}
```
We considered various options, like making `_` always be a discard in a lambda body when the
lambda parameters are discards, but we have a different precedent for non-lambda
scopes like the following:
```C#
void M()
{
int _ = 0;
{
_ = 1; // This assigns to the variable _
}
}
```
We decided a better alternative to making complex scoping rules to prevent confusing code is to
push for a warning wave to make using `_` as an identifier a warning. Essentially, if the
above behavior is confusing, the confusing aspects are best resolved by always using `_` as a
discard, rather than special language rules.
The scoping behavior is confirmed that multiple `_`s in a lambda parameter list are discards.
There are no other modifications to variable scopes i.e., they are not introduced in the lambda
body scope, but they also hide nothing from the enclosing scope).
### Common Type Specification
Proposal: https://github.com/dotnet/csharplang/issues/2823
This is revisiting a previous design question around whether to improve the common type
specification and also to target type various expressions. In the last discussion we wanted more
investigation on the impacts of adding target-typing and the consequences to improving the common
type algorithm in the future.
After investigation, there's a proposal to resolve questions about changing common type inference
by looking back to an earlier rule: never infer a type that was not one of the input types. This
is a rule that mainly comes from where to draw the line on complexity of type inference.
This is a simple rule, but it's arguable that there are certain enhancements that don't open up
to question the entire space of inference, but still satisfy simple requests, like assuming that
null and simple integer types can be inferred as nullable. This would be similar to the language
specification for nullable lifting operations on binary operators.
However, that still leaves the fundamental problem we approached in the beginning: improving
inference is a breaking change after target-typing is introduced. The proposal introduced is to
not improve type inference in the future and consider this an acceptable outcome, given that
target typing would satisfactorily resolve most of the examples given, and potentially in a
clearer way than improving the common type algorithm. This would also have the property of
preserving the original constraint of the common type algorithm, where no type is inferred that
isn't present in any of the inputs.
The biggest drawback here is that the switch expression and conditional expression would behave
differently. The conditional expression would have to preserve the common type algorithm for all
places it succeeds, for backwards compatibility.
One possible way out of this is to separate the notion of common type for backwards
compatibility, namely in overload resolution, and the inferred type, as the type used for `var`,
where no target type is available. If feasible, this would resolve the issue mentioned at the
previous meeting, where we would be unable to improve type inference without creating breaking
changes in overload resolution.
**Conclusion**
Overall, we're in favor of adding target-typing for these expression forms. We should consider if
there's anything more we would like to do to make the switch and conditional expressions more
similar.

View file

@ -1,118 +0,0 @@
# C# Language Design Meeting Notes for Oct. 30, 2019
## Agenda
1. Function pointer syntax
2. Enhancing Common Type Algorithm (again)
## Discussion
### Function pointer syntax
Proposal: https://github.com/dotnet/csharplang/issues/2917
While exploring syntax for function pointers, it turns out that parsing
the previously proposed syntax, namely
```C#
func*(int, int)
```
can feature exponential parsing behavior in the case of nested function
pointers.
An alternative is proposed using a generic-style syntax. E.g.,
```C#
func*<void>
cdecl*<int, void>
```
The proposed changes employ `*<` as being unambiguous in the language today. One suggestion is to
use the same trick, but with a minor modification: `func` would always be the first token, and
the calling convention would be the second token e.g.,
```C#
func cdecl*<int, void>
```
and the set of calling conventions would evolve as the set of CLR-supported
calling conventions evolve.
There are a couple features which aren't mentioned, and we're not sure if we
need to support them, and how we would do so.
Mainly, we don't know whether certain features, like attributes, require definitions of named
function pointers, instead of anonymous function pointer types. The runtime has proposed using
attributes to express configuration, like suppressing native/managed transition.
The current syntax doesn't have a place to put attributes, so if we wanted to support that
design, we would have to have some definition to place attributes. This would be similar to
delegate definitions, where you can define a delegate with a name and attributes, and then refer
to the delegate type by name.
However, the current runtime implementation doesn't support declaring tokens for the function
pointer, so there's no official mechanism to attach attributes to a function pointer. A follow-up
question is to ensure that the spec allows us to put the calling convention in the signature
during emit as part of the function pointer type.
We brainstormed a bunch of possible syntaxes (some more serious than others):
```C#
delegate managed *int(int, long)
delegate managed *List<T>(int, long)
delegate * List<T> managed(int, long)
delegate<managed> * List<T>(int, long)
delegate * managed List<T>(int, long)
delegate managed int *(int, long)
delegate managed*<int, long, void>
delegate *<int, long, void>
delegate* managed<int, long, void>
delegate*<int, long, void>
```
**Conclusion**
The new syntax is agreed to be unambiguous, as far as we can see. We agree on three
modifications:
1. The optional calling convention should always have a prefix.
2. Putting the `*` near the prefix is better, as it makes the function pointer
more recognizable.
3. `delegate` is a better prefix, as it is already a keyword.
Our final decision is
```C#
'delegate*' optional_calling_convention '<' type < type , ... > , return_type '>'
```
### Enhancing Common Type Specification follow-up
Proposal: https://github.com/dotnet/csharplang/issues/2823
The previous proposal was to add target-typing as a fallback to existing features,
like the conditional expression, and do no more work on enhancing the common type
algorithm.
This would solve certain problems and not others. In the case where the conditional expression
has a common type, but that type is not the one you want (e.g., `b ? 1 : 2`, but you wanted the
result to be a `byte`), target typing would not solve this.
At the same time, not having all possible improvements doesn't seem to impact the
decision for nullable, specifically. We already have special language knowledge
and handling of nullable and when language algorithms fail to introspect and find
the "obvious" solution, this feels worse than more general failures.
**Conclusion**
None today. We ran out of time and would like to talk about it more.

View file

@ -1,161 +0,0 @@
# C# Language Design Meeting for Nov. 11, 2019
## Agenda
1. Confirm removal of warning calling a method that returns `[MaybeNull]T`
2. Allow interpolated string constant
3. Enhancing the Common Type Specification
4. Type pattern
5. Simple name binding with target types
## Discussion
### Calling a method that returns `[MaybeNull]T`
We previously proposed this as a safety warning, since the value of `T` could be nullable,
similar to `default(T)`. The approach of warning on all uses turns out to be more annoying than
we thought, producing multiple false positives.
There are a couple steps to improving it. First, we'd like to not warn if the expression is
returned in a method that is also annotated to return `[MaybeNullT]`. This would require us to
use the information from the nullable attributes (or at least `[MaybeNull]`) inside the method.
We also have to implement a three flow state design for nullable analysis, since it's not good
enough to know whether the flow state is maybe null or not null (since all unconstrained generics
are already considered maybe null), we need to know if the state is maybe null, even if the
substituted generic does not allow null.
**Conclusion**
Confirmed that the warning can be removed. This will make it likely that we will produce more
warnings in the case that all of these component parts are working, but we're hoping that almost
all of these warnings will be real safety issues, not false positives.
### Interpolated string constant
https://github.com/dotnet/csharplang/issues/2951
The simplest form of this proposal is to just allow constant strings with `$` in front of them.
For example,
```C#
const string s = $"abc";
```
This doesn't really seem very useful, except in refactoring where you had an interpolated string,
and made it constant, and it happened to not have any substitution.
A more useful version would allow constant interpolated strings if all of the substitutions are
constant strings.
**Conclusion**
Seems reasonable. We'll put it in "any time". @agocke to champion.
### Enhancing the Common Type Specification (again)
https://github.com/dotnet/csharplang/issues/2823
We discussed a number of improvements, notably using target-typing as a fallback when there is no
common type. The idea to improve target-typing for nullable seems interesting, but we need a more
detailed proposal to examine. There's a worry that this will significantly complicate type
inference, which needs investigation.
There are a few associated questions, namely:
1. Do we improve the common type algorithm to not find a common type if all the component
types cannot be converted to it? (e.g., `1` and `null` would have no common type)
2. Do we allow improve common type inference for the cases where there is no target type (e.g.,
when assigned to `var` or a method that takes a generic `T`)
3. Do we allow target typing when the common type is not convertible to the target? (e.g.,
`byte b = cond ? 1 : 2` would fall back to target typing because `int` is not convertible to
`byte`)
There's also the behavioral difference between the old expressions which have target-typing as a
fallback, and the switch expression, which currently prefers the target-typing if one exists.
Some of the questions above, including (3), would make the two behaviors be very similar, in that
target-typing would be present in the few cases that rely on it.
**Conclusion**
We agree to the following changes:
1. Target-typing as a fallback for the old expressions (array creation, conditional expression, et al.)
2. We want target-typing to succeed in the case where the common type does not convert to the
target-type
3. We want to change switch expression to use the natural type if it converts to the target-type,
and only use target-typing if the natural type does not convert to the target.
We want to continue to investigate improvements to the common type algorithm, including changes
to support nullability.
### Type patterns
https://github.com/dotnet/csharplang/issues/2925
The first proposal is to simplify some existing constructs and add some simplification to some
current behavior by creating a simple "type pattern" without a variable introduction.
```C#
void M(object o1, object o2)
{
var t = (o1, o2);
if (t is string) // This is currently legal and we would just change the spec to say that this
// is a type pattern, although the `is` would prefer a type in an ambiguity
// to preserve backwards compatibility
if (t is (int, string)) // This would now be legal as a tuple pattern, containing two type patterns
switch (t)
{
case string: // this would now work, but would be the inverse of `is`, preferring a constant
// for backwards compatibility
}
}
```
This has broad agreement as a good change, and multiple LDM members have been frustrated that the
switch statement and expressions often require a `_` in these places.
**Conclusion**
The first proposal, allowing simple type patterns, is broadly useful right now. Accepted for the
C# 9.0 milestone.
### Name lookup for binding simple names with known target type
https://github.com/dotnet/csharplang/issues/2926
The second proposal is to adjust binding with a known target type, so you could say
```C#
class Outer
{
public class Inner1 : Outer {}
public class Inner2<T> : Outer {}
}
int M1(Outer o) => o switch
{
Inner1 _ => 0, // Binds to Outer
Inner2<int> _ => 1;
}
```
or even
```C#
Color x = b ? Red : Greed;
var y = b ? Color.Red : Greed;
```
**Conclusion**
This change feels especially natural in switch statements and expression with enums. It seems
useful in the other contexts as well. The extension to nested classes would be particularly
relevant to a discriminated union based off of nested classes.
There's a lot here in the binding changes. Let's sit on it a bit and take a look with a
discriminated unions proposal. Triaged to C# 9.0 to match discriminated unions.

View file

@ -1,114 +0,0 @@
# C# Language Design Meeting for Nov. 13th, 2019
## Agenda
1. Discriminated unions
2. Improved analysis of `[MaybeNull]T`
## Discussion
### Initial discriminated union proposal
https://github.com/dotnet/csharplang/blob/master/proposals/discriminated-unions.md
Questions around some restrictions:
1. Why can't you have constructor parameters on the enum class type?
- No real reason, except worries about inheritance aside from members
- Keeping inheritance syntactically close by is considered a feature of the proposal,
since the user can easy analyze what all the possible children of the root are
- Syntax for the base call in value members would have to be specified
2. How `partial` do we want to allow things? There's good reason to put all the members together
3. Are records short enough syntax that we don't need the "simple" type syntax?
- Regardless of brevity, if members are required to explicitly inherit from the root this seems
like purely useless work, since doing otherwise would be an error
4. What if you want to define just regular nested classes, that don't inherit from the root type?
5. Scoping for nested enum class members? The proposal for altering binding wouldn't help with
nested members, because it only applies to simple names.
```C#
enum class Expr
{
enum class BinaryOperator
{
Add(Expr left, Expr right),
Multiply(Expr left, Expr right)
}
}
Expr M(Expr left, Expr right)
{
return Add(left, right); // error, no name "Add" found under Expr
}
int M(Expr e) => e switch
{
Add _ => 0, // error, no name Add found under Expr
}
```
There's also a proposed alternate syntax that would let you put everything directly into the enum
class:
```C#
enum class Color
{
Red,
Greed; // semicolon delimiting end of enum class cases, and start of regular members
public static int HelperField;
public static int HelperProperty => 0;
public int HelperMethod() => 0;
}
```
## Improved analysis of `[MaybeNull]T`
Proposal: https://github.com/dotnet/csharplang/issues/2946
The new proposed abstract domain for nullable analysis would be:
```C#
enum
{
NotNull,
MaybeNull,
MaybeNullEvenIfNotNullable
}
```
If we improve analysis of `MaybeNull` inside the method, do we want to push the new functionality
through for all the nullable attributes at the same time? This seems useful from a consumer
perspective, in that you get the different warnings all at the same time, but there's also the
simple question of resources and scheduling.
Similarly, if we do improve `MaybeNull` analysis in the body of the method, do we need to improve
the analysis in other places, like overriding, at the same time? Or are we OK with a piecemeal state
where `MaybeNull` is enforced in the method body but not in overriding? Or where we enforce `MaybeNull`
in overriding, but not other attributes like `DisallowNull`?
A broader question is how common `MaybeNull` is in general. Our experience in the .NET Core mscorlib
is that there are only 10 occurrences of `MaybeNull`, and even when broadening to LINQ, there are at
most a few dozen occurrences, mostly in places that may return a `default` value. In comparison,
`NotNullIfNotNull` has ~30 occurrences in mscorlib.
On the other hand, `default(T)` is very common and is an instance of `MaybeNull` in a sense. This implies
that the flow state of `MaybeNull` is particularly important.
An additional question is whether or not to revive the `T?` proposal to deal with the historical problems
around `MaybeNull` annotations on locals and other places where attributes are not permitted.
**Conclusion**
We think improving any aspect of the attributes can be considered independently and we will not
require the changes to ship together. We think that proposal is good. Accepted.
No statement on `T?`. We will schedule a discussion soon to get a permanent decision on the proposal.

View file

@ -1,73 +0,0 @@
# C# LDM for Nov. 18th, 2019
## Agenda
1. More patterns
## Discussion
### More patterns
Parenthesized patterns:
- What about ambiguity with a potential 1-tuple pattern?
- There was a previous ambiguity here with a parenthesized constant expression,
so matching a 1-tuple pattern requires resolving ambiguity, for example by adding
tuple names: `x is (Item1: var tupleVal)`
Relational patterns:
We don't love the syntax. The problem is really that we're using a binary operator in
a unary context. On the other hand, it's useful in that it's easy to adjust for closed
or open bounds by adjusting the inclusiveness of the operator.
The other shaky part of the syntax is support for user-defined operators. One of the
fundamental parts of pattern matching is that the semantics are well-understood, meaning
that the switch expression expects certain things must be true, like that checks must return
the same value for the same inputs and can be called any number of times. We have a few patterns
that execute user-defined code, but it seems more likely that they're usually safe (property
getters and Deconstruct).
For `==` and `!=` specifically, they seem redundant and likely to cause confusion when there is a
user-defined operator that is not supported. Consensus is to remove support in relational patterns.
The other confusing part is conversion, where the input type does not statically contain a built-in
the binary operator. For instance,
```C#
object o = (short)1;
_ = o switch
{
< 3 => 0, // This would be false, since `o` is a short, not an int, and the
// unconverted '3' is an `int`
};
```
This is already how constant patterns work, so there may be some confusion here already, but we're
worried about broadening the problem by adding the `not` pattern in combination with the relational
operators. For example, if you match `not < 3`, this would evaluate to true, but not because the
value is not less than three, but because it is not an int. This would mean that `>= 3` and `not < 3`
would be different, since the type check can play into the check.
However, we don't have any better approaches, and the hope is that this will be a relatively rare
occurrence. If the input pattern has a statically known built-in operator this would not be a concern.
The proposal states that the input type of the expression must contain a conversion that is not a boxing
conversion. However, we do support constant pattern checks for unconstrained type parameters, so we
need to change the proposal to allow unconstrained type parameters as well.
### Pattern combinators
The proposal contains three breaking changes:
- `not` is considered a type in C# 8 and a pattern in C# 9.
- `and` and `or` are allowed as variable names in C# 8, but are pattern combinators in C# 9
An important question is whether these are allowed and how they are breaking. Changing behavior is
probably a bridge too far. We would consider providing an error in the old scenarios and forcing
disambiguation.
We'll need more discussion on the breaking changes and how we can mitigate them.

View file

@ -1,126 +0,0 @@
# C# Language Design Notes for Nov 25, 2019
## Agenda
1. Allow `T?` where `T` is a type parameter that's not constrained to be a value or reference type
# "Unconstrained" `T?`
In C# 8.0 we entertained the possibility of allowing `T?` on type parameters `T` that are unconstrained, or constrained only in a way that they can still be instantiated with both value and reference types.
The idea was to accurately express the type of `default(T)`. To this end, `T?` would end up being the same as `T`, except when `T` is instantiated with a nonnullable reference type `C`, in which case `T?` is `C?`. The reason is that for non-nullable reference types `C`, `default(C)` is still null, and hence of the type `C?`, not `C`.
`T?` would, for instance, allow the return type of methods like `FirstOrDefault()` to be better expressed:
``` c#
public T? FirstOrDefault<T>(this IEnumerable<T> src);
```
In the generic context where `T` is in scope, the meaning of `T?` would be "may be null, even when `T` is not nullable". The expression `default(T)` would no longer warn, but the type of it would be `T?`, and assigning `T?` to `T` *would* warn.
In the end we decided not to allow `T?` due to a couple of problems which we'll get back to below. Instead we addressed many of the scenarios with the addition of nullable attributes such as `[MaybeNull]`, which can be used to annotate e.g. the `FirstOrDefault()` method:
``` c#
public [return:MaybeNull] T FirstOrDefault<T>(this IEnumerable<T> src);
```
## Recent changes
Default values keep causing grief, and we've recently [(Nov 13)](https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-11-13.md#improved-analysis-of-maybenullt) decided on some changes to handle them:
1. Add a third null-state next to "not null" and "maybe null", which is "maybe null *even* when the type doesn't allow", and make that the null state of `default(T)` for an unconstrained type parameter `T`, as well as results of method calls etc. (such as `FirstOrDefault`) that were annotated with `[MaybeNull]`
2. Remove the "W warning" on assignment of such values to locals of type `T`, but instead subsequently track the new state *for* those locals, which may subsequently lead to warnings if used unsafely
3. Remove warnings when producing such values as a result (e.g. return value) that is itself annotated as `[MaybeNull]`
Put together, these three eliminate most of the friction around the use of `default(T)` and `[MaybeNull]`. However, they also get very close to the previously proposed semantics of allowing `T?` on unconstrained `T`. We could take this two ways:
1. Since we mostly succeeded in addressing the problem without `T?`, it further reduces the need for it.
2. This shows that "the type of `default(T)`" is an important concept that should be properly reified in the language instead of tricks with attributes.
## Taking another look at "unconstrained" `T?`
The main arguments in favor of a syntax for "the type of `default(T)`" even with the above changes are:
1. By adding "it" as a tracked null state, we're already half embracing it, but by leaving it inexpressible as a type we need to fall back on "tricks" to make use of it
2. `[MaybeNull]` doesn't help when "the type of `default(T)`" is needed in a constructed (array, generic, tuple, ...) type
The use of the syntax `T?` to mean "the type of `default(T)`" as two significant problems as well, which ultimately led us to not embrace it for C# 8.0:
1. Its meaning is different from the meaning of `T?` when `T` is constrained to be a value type.
2. Overrides cannot restate their constraint, and `T?` in those today always means the value type kind.
## Problem 1: `T?` and `T?` mean different things
The first problem is not technical, but one of perception and language regularity. Consider:
```
public T? M1<T>(T t) where T : struct;
public T? M2<T>(T t);
var i1 = M1(7); // i1 is int?
var i2 = M2(7); // i2 is int
```
The declaration of `M1` is legal today. Because `T` is constrained to a (nonnullable) value type, `T?` is known to be a nullable value type, and hence, when instantiated with `int`, the return type is `int?`.
The declaration of `M2` is what's proposed to allow. Because `T` is unconstrained, `T?` is "the type of `default(T)`". When instantiated with `int` the type of `default(int)` is `int`, so that is the return type.
In other words, for the same provided `T` these two methods have *different* return types, even though the only difference is that one has a constraint on `T` but the other does not.
The cognitive dissonance here was a major part of why we didn't embrace `T?` for unconstrained `T`.
## Problem 2: pseudo-"unconstraint" in overrides for disambiguation
In C# overrides of generic methods cannot re-specify constraints, but must inherit them from the original declaration. Before C# 8.0, when `T?` appeared in signatures of such overrides, it was always assumed to mean the nullable value type `Nullable<T>`, because what else could it mean?
In C# 8.0 `T?` acquired the possible second meaning of nullable reference type. In order to disambiguate the search for the original declaration, we reluctantly introduced the ability to specify "pseudo-constraints" on the overrides: When `T` was a type parameter constrained to be a reference type, you can now say so by adding `where T: class` to the declaration. If you want the default assumption of it being constrained to a value type to be made explicit, you can also optionally specify `where T: struct`.
These helper constraints are only there to help find the right declaring method up the inheritance chain (which may be overloaded). The *actual* constraint that's emitted into generated code is still the one that's inherited from the original declaration, once that one has been identified. Thus, only `class` and `struct` can be used as constraints on an override.
Now here comes a third `T?`, the defining characteristic of which is that it is *not* constrained to be either a reference or value type. To distinguish it from the other two in an override of a generic method, we would need a third "constraint" that is actually an *unconstraint* - that specifically says that it is *not* constrained!
## Solutions
We have two general approaches to address these problems (beyond the ever-present solution of giving up on the feature):
1. Live with problem 1, and try to explain things as best we can. Find a syntax to express the "unconstraint" of problem 2.
2. Use a different syntax than `T?` to express "the type of `default(T)`".
## Solution 1
A brain storm for solution 1 syntaxes yielded:
``` c#
override void M1<[Unconstrained]T,U>(T? x) // a
override void M1<T,U>(T? x) where T: object? // b
override void M1<T,U>(T? x) where T: unconstrained // c
override void M1<T,U>(T? x) where T: // d
override void M1<T,U>(T? x) where T: ? // e
override void M1<T,U>(T? x) where T: null // f
override void M1<T,U>(T? x) where T: class|struct // g
override void M1<T,U>(T? x) where T: class or struct // h
override void M1<T,U>(T? x) where T: cluct // joke
```
Of these we have a vague preference for c, expressing explicitly that there is *not* a constraint, closely followed by b, which is the most general constraint one could state. None of the others resonated.
## Solution 2
A brain storm for solution 2 syntaxes yielded:
``` c#
void M1<T,U>(default(T) x) // X
void M1<T,U>(default<T> x) // Y
void M1<T,U>(T?? x) // Z
```
Solutions X and Y try to be explicit about the type meaning "the type of `default(T)`", but run the risk of implying "the result *is* `default(T)`". Solution Z is mostly just the shortest `?`-like token we could think of that isn't `?`.
We unanimously preferred Z. `??` can be read as *may be* (`?`) nullable (`?`).
## Conclusion
Comparing the two approaches we do favor solution 2, adopting the syntax `T??` to mean "the type of `default(T)`" (when `T` is unconstrained). We do think that this is the best solution, because it addresses both problems, is terse, and looks reasonable. We will move ahead with prototyping and fleshing it out.
We would probably only allow it to be used at all on type parameters `T` that are not constrained to be either value or reference types. That way you can never use either `??` or `?` on the same thing.

View file

@ -1,117 +0,0 @@
# C# Language Design Meeting for Dec. 11, 2019
1. Design review feedback
## Discussion
We got feedback from the design review that we shouldn't try to conflate too many problems. If
we want to make it easier to support structural equality, we should see if it's possible to
address directly, without requiring the other features of records. One suggestion was to take
inspiration from `VB`, which allows the `key` modifier to be added to VB anonymous types to
indicate structural equality with the members used as the keys.
We took that advice and looked at a sketch of what that could look like:
```C#
class C
{
public key string Item1 { get; }
public string Item2 { get; }
}
```
The `key` modifier would be used to control generated equality, such that all members marked
`key` would be compared for equality in an `Equals` override (using the same pattern as in the
original records proposal).
The above code sample certainly looks simple, but unfortunately it's not sufficient for
real-world code. Both `Item1` and `Item2` are `get`-only autoproperties, meaning that as-is there
is no way to initialize those members. A working example looks more like:
```C#
class C
{
public key string Item1 { get; }
public string Item2 { get; }
public C(string item1, string item2)
{
Item1 = item1;
Item2 = item2;
}
}
```
This is significantly longer than the original sample, grows with the number of properties, and
is repetitive. The latter is particularly problematic, as repetitive boilerplate is often a source
of hard-to-see bugs or typos.
Worse, we've seen that when construction becomes laborious, users resort to making their types
mutable instead of writing out the constructor, e.g.
```C#
class C
{
public key string Item1 { get; set; }
public string Item2 { get; }
}
```
This is unfortunate in most code, but it's worrying when combined with structural equality. When
used in Dictionaries, mutable types with structural equality are an anti-pattern because the hash
code of the type changes, causing the type to "disappear" from the Dictionary. It's one thing if
the user opts-in to this risk, knowing that they need to be careful, and a completely different
situation if the language encourages a dangerous pattern.
**Conclusion**
This is an interesting design point that we think we'll incorporate. We've also agreed that we
have a hard design requirement: if we provide a feature for easy structural equality, we must
provide a more convenient syntax for constructing immutable types in the same release. To do
otherwise would be to effectively make a trap for users.
## Nominal vs Positional
We also continued to explore the space of nominal vs. positional records. An insight from the
previous discussion is that nominal vs. positional records are really about improving syntax
for type construction. Positional records are about taking the existing type construction,
constructors, and giving them a shorter syntax. Nominal records are about identifying some
weaknesses of the existing construction system and providing a new feature to support them. In
both cases, though, the proposed features shorten construction by avoiding repeating the member
declarations and the assignments.
We also had the following notes, in no particular order:
* For positional constructors, it's important to consider primary constructors. We have one
proposal for primary constructors, but have not had a discussion on whether we want them, and if
the syntax is worth taking away from the record syntax.
* If the `initonly` keyword is required, even for common cases, this is about
as expensive syntax-wise as a setter. It may be a few more characters, but it
keeps things on one line, as opposed to the multi-line constructors that are
required right now.
* There's an opposition between evolving existing data types and picking and choosing features when
you need them, but also providing a simple syntax for the most common case. Certainly positional
records solve a common case. The question is how common that case is.
* Positional records use existing initialization strategy (construction) which is fairly
well-understood. The initonly feature, by contrast, will force us to reexamine some assumptions.
For instance, constructors take all inputs at once, meaning that you can enforce requirements
between them at construction time. `initonly` overrides properties after construction, and
one-by-one, so it's not possible, or not obvious, how to provide this functionality.
* There are mixed feelings on the requirements of what features will be available individually, and
which are separable. Some people feel that addressing the most common case is sufficient for
providing the value of the feature, namely that there can be a single "record" which provides
immutable construction and value equality, and that is the only way to access these features.
Others think that the features need to be adoptable independently: existing types need to be able
to adopt generated equality without adopting immutable construction, while immutable construction
needs to be available without structural equality.
One conclusion is that no proposal will solve all record-related problems. This is fine. We also
have general agreement that there should be a convenient shorthand for the combination of most or
all of the features (simple immutable construction, structural equality, etc.). Notably we don't
all agree on whether or not structural equality will be the most common type of equality for
records, but the shortest record form may include structural equality for other language design
reasons.

View file

@ -1,122 +0,0 @@
# C# Language Design Notes for Dec. 16, 2019
## Agenda
1. Switch expression as a statement expression
2. Triage
## Discussion
### Switch expression as a statement expression
https://github.com/dotnet/csharplang/issues/2860
The proposal being discussed is whether to allow the switch expression without a discard:
```C#
_ = a switch
{
...
};
// becomes
a switch
{
...
};
```
One of the against is that it makes for a confusing decision in the language as to whether you
use a switch expression or switch statement. Right now the guidance is simple: if you are in a
statement context, use a statement. If you have an expression context, use a switch expression.
Now, for a statement, you could use either a switch statement, or a switch expression in
statement form. It's not clear which.
One way of resolving this is that these are two parallel features that provide similar features
in a slightly different syntax and semantics. Then the answer simply becomes, use whichever one
you like better. If the new switch expression form includes exhaustiveness checking, that would
be a reason to use or not to use it, aside from the syntax differences. Similarly, the switch
statement ability to `goto` another case is a reason to use that form. However, if we accept
that, the switch expression feels artificially limited. To provide a satisfactory parallel
feature we have to augment the switch expression to allow for statements in the switch arms. Then
there is a potential new set of features: block in switch expressions.
On the other hand, this feels like feature creep. The original proposal was quite simple: allow
users to elide `_ =` and remove the requirements for the arms to have a common type. While we may
want to have a number of different new features for switch expression to make it comparable to
the switch statement, there's value in doing the feature as-is, and adding those features later.
This is contingent on us being fairly confident that the new features can be added without
breaking changes, but there's a fair amount of confidence that we know where we would go with the
feature. This perspective would require us to keep certain behaviors to ensure that the switch
expression keeps its differences from the switch statement. For instance, the new switch
expression-as-statement would have to check exhaustiveness if we see it as a strict improvement
for the switch expression.
Lastly, we all find the proposed switch expression-as-statement requiring a semicolon i.e.,
```C#
a switch
{
b => ...,
c => ...
}; // semicolon required
```
as being extremely ugly.
**Conclusion**
Rejected as-is. We'd be interested in a new proposal on this topic, addressing many of the
concerns that we brought up today.
### Triage
#### Definite assignment of private reference fields
**Conclusion**
Accepted for warning waves v1, wherever that is triaged.
#### Remove restriction on yielding a value in the body of a try block
Also for async iterators.
Issue #2949
**Conclusion**
Accepted, Any Time.
#### Generic user-defined operators
Issue #813
**Conclusion**
There's no syntax in the invocation to specify the type arguments, in case inference doesn't
succeed, and we think almost any syntax in the invocation location would be ugly. In addition, we
don't have a lot of examples of why this would be significantly better than alternatives (like
writing a method).
Rejected.
#### Support for method argument names in `nameof`
Issue #373
It looks like there's a significant breaking change if we allow the parameter names to be in
scope generally.
```C#
const int p = 3;
[Attribute(Property = p)]
void M(int p) { }
```
If we just allow `nameof` to have special scoping to allow the names in the method declaration to
be in scope, then there's no language breaking change. The scoping rules would prefer names in
the method header (including type parameters) over rules in the rest of the program.
**Conclusion**
Accepted, Any Time.

View file

@ -1,147 +0,0 @@
# C# Language Design Meeting for Dec. 18, 2019
## Agenda
1. Nullable issues
a. Pure null checks
b. Consider `var?`
## Discussion
### Pure null checks
We have the following syntax which semantically check for null in the language:
* `e is null`
* `e == null`
* `e is {}`
* `e is object`
However, for nullability we have a separate notion of a "pure" null check that
causes a null branch to appear, even if the variable being tested was declared
not null, or vice versa.
An example of why this would matter is
```C#
var current = myLinkedList.Head; // assume Head is nullable oblivious/unannotated
while (current is object)
{
...
current = current.Next; // assume oblivious
}
current.ToString(); // only warns if `is object` is a pure null test
```
We previously established that all "pure" null checks contain the word `null` in them, meaning
that only `e is null` and `e == null` are pure null checks today. There is a proposal that we
should unify all forms that are semantically equivalent, regardless of syntax.
There is also a proposal to expend this even to places which are not "pure" checks, i.e.
they have semantic effect larger than just checking for null. For instance, we could
also check `e is object o`, which also introduces a variable `o`. We came up with
the following list of potential checks:
```
e is null // pure
e is object // Proposed
e is [System.]Object // Proposed
e is object _
e is object x
s is string // s denotes expr of static type string
s is string x
o is string x
e is {} // Proposed
e is {} _
e is {} x
e == null // pure
e != null // pure
e is not null // pure
e is not object // etc...
```
All parties argue that other positions are confusing as to why something is a pure
null check and something else, that's very similar, is not. It seems like drawing
any particular line will always imply that something similar could be confusing.
One difference between versions that check between a semantically pure null check, i.e. a piece
of syntax that has no other meaning than testing for null, is that if there is a pure null check
then any warning is definitely a bug in user code: either the check is superfluous, or there is
an actual safety issue. If the check is not pure, there may not be a bug, because the check may
not actually be superfluous and this may be a spurious warning.
Given that the pure null checking is useful, it's mainly about finding the right balance between
helping the user find bugs in their code and finding a set of rules that are also easily
understandable. The main argument against broadening beyond our current rule is that "pure checks
contain the word 'null'" is a simple rule, and adding warnings in an update is a heavy way to
address the issue.
On the other hand, we have changed nullable warnings multiple times already, plan to
do it again, and have warned people that nullable warnings may be in flux for a time.
If the feature is also meant to react to user intent, and if we believe `x is object`
is intended by the user to be a null check, then making it a pure null check would
be correctly responding to user intent.
We could also decide based on whether or not we want to suppress certain patterns. If
we believe `x is object` or `x is {}` aren't good ways to test for null, then making
them not pure null checks would encourage users not to use it. This did not seem a
compelling position for anyone in LDM.
**Conclusion**
We agree that we should broaden the set of pure null checks. We agree that `x is object` should
be a pure null check. Moreover this should be based on the type in the `is` expression, meaning
that any type `T` that binds to `System.Object` in `x is T` would be a pure null check. We also
agree that `x is {}` is a pure null check.
None of `x is object _`, `x is object o`, `x is {} _`, or `x is {} o` are pure null checks.
### `var?`
At this point we've seen a large amount of code that requires people spell out the
type instead of using var, because code may assign `null` later.
An example,
```C#
var current = myLinkedList.Head; // annotated not null
while (current is object)
{
...
current = current.Next; // warning, Next is annotated nullable, but current is non-null
}
```
One way to deal with this is to allow `var?`,
```C#
var? current = myLinkedList.Head;
// now current is nullable, but the flow state is non-null
current.ToString(); // no warning, because the flow analysis says it's not null
```
This would let people express that the think the variable may be assigned null later on.
On the other hand, we could just permit these assignments when using `var`, and use flow analysis
to ensure safety.
```C#
var current = myLinkedList.Head;
current = null; // no warning because var is nullable
current.ToString(); // warning, the flow state says this may be null
```
This would allow users to be explicit when they want to make sure not to assign
null to a type, but they have to spell out the type.
**Conclusion**
Make `var` have a nullable annotated type and infer the flow type as normal.

View file

@ -1,145 +1,69 @@
# Upcoming meetings for 2019
## Schedule ASAP
## Schedule when convenient
- removing support for `#pragma warning enable` (https://github.com/dotnet/roslyn/issues/36550) (Julien)
- disallow `expr!!` and use `parameter!!` (instead of `parameter!`) (Mads)
- allow `#nullable` to mean `#nullable enable` (Julien)
- effect of `[DoesNotReturn]` on various flow analyses? (https://github.com/dotnet/roslyn/issues/37081) (Julien)
- suppressing LHS of compound assignment (https://github.com/dotnet/roslyn/issues/36617) (Julien)
- warn on `[NonNull]` and other attributes outside of nullable context (https://github.com/dotnet/roslyn/issues/36588) (Julien)
- confirm suppression on `ref` arguments (https://github.com/dotnet/roslyn/issues/35555) (Julien)
- confirm features that are pushed out:
- nullable attributes do not affect method bodies (`[AllowNull] T M() { return default; }`)
- nullable attributes not checked in OHI
- no diagnostics on misused nullable attributes
- async LINQ (Julien)
- Syntax of positional records/primary constructors (Andy)
- Discussion of refreshing language spec (Neal)
- Conceptual model for tuples (Mads, Neal)
- Close on flag for warning waves (existing `/warn` or new `/warnVersion`?)
## Recurring topics
- *Triage championed features*
- *Triage milestones*
- *Design review*
## Sep 25, 2019
## Sep 18, 2019
## Sep 16, 2019
## Sep 11, 2019
- Close https://github.com/dotnet/roslyn/issues/37313 (Rikki, Phillip)
- Triage new proposed [9.0 features](https://github.com/dotnet/csharplang/issues?q=is%3Aopen+is%3Aissue+no%3Amilestone+label%3A%22Proposal+champion%22)
- Finish triaging away [8.X milestone](https://github.com/dotnet/csharplang/issues?q=is%3Aopen+is%3Aissue+label%3A%22Proposal+champion%22+milestone%3A%228.X+candidate%22)
## Sep 4, 2019
- https://github.com/dotnet/roslyn/issues/37313 (Rikki, Phillip)
## Aug 28, 2019
- Triage language features
## Aug 26, 2019
- Triage language features
## May 22, 2019
- Confirm whether we want `abstract` modifier to be implied for interface implementations inside derived interfaces unless there is an implementation.
## May 1, 2019
* Nullable attributes - continue discussion
* Nullable opt-in - do we need to adjust the story?
# C# Language Design Notes for 2019
Overview of meetings and agendas for 2019
## Dec 18, 2019
[C# Language Design Notes for Dec 18, 2019](LDM-2019-12-18.md)
1. Pure null checks
2. `var?`
## Dec 16, 2019
[C# Language Design Notes for Dec 16, 2019](LDM-2019-12-16.md)
1. Switch Expression as a Statement Expression (continued) (Neal, Fred)
2. Triage
## Dec 11, 2019
[C# Language Design Notes for Dec 11, 2019](LDM-2019-12-11.md)
1. Design review feedback
## Dec 9, 2019
(not yet transcribed)
- https://github.com/dotnet/csharplang/issues/2850 Proposed changes for pattern-matching (continued) (Neal)
- https://github.com/dotnet/csharplang/issues/2860 Switch Expression as a Statement Expression (Neal)
## Dec 4, 2019 (Design Review)
(not yet transcribed)
## Nov 25, 2019
[C# Language Design Notes for Nov 25, 2019](LDM-2019-11-25.md)
1. Revisit unconstrained `T?` (Mads, Jared)
## Nov 20, 2019
(not yet transcribed)
- https://github.com/dotnet/csharplang/issues/2911 Utf8 String Literals (Neal)
- https://github.com/dotnet/csharplang/issues/2850 Proposed changes for pattern-matching (continued) (Neal)
## Nov 18, 2019
[C# Language Design Notes for Nov. 18, 2019](LDM-2019-11-18.md)
1. Proposed changes for pattern-matching
## Nov 13, 2019
[C# Language Design Notes for Nov 13, 2019](LDM-2019-11-13.md)
1. Discriminated unions
2. Improve analysis of `[MaybeNull]T` values
## Nov 11, 2019
[C# Language Design Notes for Nov 11, 2019](LDM-2019-11-11.md)
1. Confirm removal of warning calling a method that returns `[MaybeNull]T`
2. Allow interpolated string constant
3. Enhancing the Common Type Specification
4. Type pattern
5. Simple name binding with target type
## Oct 30, 2019
[C# Language Design Notes for Oct 30, 2019](LDM-2019-10-30.md)
1. Function Pointer syntax
2. Enhancing the Common Type Specification
## Oct 28, 2019
[C# Language Design Notes for Oct 28, 2019](LDM-2019-10-28.md)
1. Discard parameters in lambdas and other methods
1. Enhancing the common type algorithm
## Oct 23, 2019
[C# Language Design Notes for Oct 23, 2019](LDM-2019-10-23.md)
1. New primitive types
https://github.com/dotnet/csharplang/issues/435
## Oct 21, 2019
[C# Language Design Notes for Oct 21, 2019](LDM-2019-10-21.md)
1. Records
2. Init-only members
3. Static lambdas
## Sep 18, 2019
[C# Language Design Notes for Sep 18, 2019](LDM-2019-09-18.md)
Triage:
1. Proposals with complete designs:
- https://github.com/dotnet/csharplang/issues/1888 Champion "Permit attributes on local functions"
- https://github.com/dotnet/csharplang/issues/100 Champion "Target-typed new expression"
2. Target typing and best-common-type features:
- https://github.com/dotnet/csharplang/issues/2473 Proposal: Target typed null coalescing (??) expression
- https://github.com/dotnet/csharplang/issues/2460 Champion: target-typed conditional expression
- https://github.com/dotnet/csharplang/issues/881 Permit ternary operation with int? and double operands
- https://github.com/dotnet/csharplang/issues/33 Champion "Nullable-enhanced common type"
## Sep 16, 2019
[C# Language Design Notes for Sep 16, 2019](LDM-2019-09-16.md)
- Support for Utf8 strings
- Triage remaining features out of the [8.X milestone](https://github.com/dotnet/csharplang/issues?q=is%3Aopen+is%3Aissue+milestone%3A%228.X+candidate%22)
## Sep 11, 2019
[C# Language Design Notes for Sep 11, 2019](LDM-2019-09-11.md)
1. Close https://github.com/dotnet/roslyn/issues/37313
2. Triage new proposed [9.0 features](https://github.com/dotnet/csharplang/issues?q=is%3Aopen+is%3Aissue+no%3Amilestone+label%3A%22Proposal+champion%22)
2. Finish triaging away [8.X milestone](https://github.com/dotnet/csharplang/issues?q=is%3Aopen+is%3Aissue+label%3A%22Proposal+champion%22+milestone%3A%228.X+candidate%22)
## Sep 4, 2019
[C# Language Design Notes for Sep 4, 2019](LDM-2019-09-04.md)
1. AllowNull on properties https://github.com/dotnet/roslyn/issues/37313
## Aug 28, 2019
[C# Language Design Notes for Aug 28, 2019](LDM-2019-08-28.md)
1. Triage language features
## Jul 22, 2019
[C# Language Design Notes for July 22, 2019](LDM-2019-07-22.md)

View file

@ -1,128 +0,0 @@
# C# Language Design Meeting for Jan. 6, 2020
## Agenda
1. Use attribute info inside method bodies
1. Making Task-like types covariant for nullability
1. Casting to non-nullable reference type
1. Triage
## Discussion
### Use attribute info inside method bodies
Examples:
1.
```C#
bool TryGetValue<T>([MaybeNullWhen(false)]out T t)
{
return other.TryGetValue(out t); // currently warns
}
```
2.
```C#
[return: MaybeNull]
T GetFirstOrDefault<T>()
{
return null; // currently warns
}
```
3.
Overriding/implementation
```C#
class A<T>
{
[return: MaybeNull]
virtual T M();
}
class B : A<string>
{
override string? M(); // warns about no [MaybeNull]
}
```
We don't have a complete design here, but some cases have an intuition about the correct
behavior. In overriding, specifically, we need a specification for what it means for an
annotation to be "compatible" with each of the attributes. On the other hand, it's not clear what
the behavior of `MaybeNullWhenTrue` should be in all cases.
**Conclusion**
We'd like to do this if the return on investment seems worth it, but to fully evaluate
we need a proposal of the work.
### Making Task-like objects nullable covariant
This is a pretty common pain-point, and it's not the first time we special-cased variance,
specifically `IEquatable<T>` is treated as nullable contravariant. It's unfortunate that the CLR
doesn't have capability to make `Task<T>` full covariant, but handling even nullable alone would
be useful. Moreover, if we later get the capability to mark `Task<T>` covariant, this would not
harm the effort.
We also think that there may be some special cases introduced for overload resolution where we
consider `Task<T>` as covariant already. If we could reuse that knowledge, that would be useful.
**Conclusion**
Let's see if we can dig up the overload resolution changes for Task-like types and try to adapt
the same rule for making Task-like types nullable covariant.
### Casting to non-nullable reference type
Example:
```C#
BoundNode? node = ...;
if (node.Kind == BoundKind.Expression)
{
var x = (BoundExpression)node; // warning if node is nullable
}
```
The question is if this warning is valuable, or annoying. We've hit this most often in Roslyn
when using the pattern `(object)x == null` to do a null check while avoiding the user-defined
equality check. This is annoying in the Roslyn codebase, but not very common outside it. On the
other hand, there's feeling that when doing the BoundNode to BoundExpression check, which is less
common in Roslyn but more common generally, there's agreement that the warning is useful in
making the type annotated with the most accurate representation of the null state.
**Conclusion**
Keep the warning, no change. We think the warning is valuable for the non-null-check cases. Newer
version of C# have features that address the null check problem and Roslyn should move to use `x
is null` or similar.
## Triage
Three related proposals: #3037, #3038, #377.
These all deal with the general problem of statements in expressions, especially statements in
switch expressions, and switch expression form in statements.
They don't necessarily require each other, but they fit a lot of the same syntax and semantic
space, so we should consider them all together.
There's also a sketch for how we could unify the syntax forms of all of three proposals, with
potential syntax changes.
**Conclusion**
We agree that all of these proposals are addressing important scenarios, and some improvement
here is valuable. We're not sure where we want to go with generalized statements-in-expressions
vs. adding special syntax forms for switch expression/statement.
We're mainly concerned that if we do switch expression blocks, we want to make sure that the we
don't block a future generalization to all expressions. We need to find a generalization that we
like, reject a generalization and accept this syntax, or put these improvements on the
back-burner if we think that a generalization is possible, we just haven't found it.
Accepted for C# 9.0 investigation.

View file

@ -1,188 +0,0 @@
# C# Language Design Meeting for Jan. 8, 2020
## Agenda
1. Unconstrained type parameter annotation
2. Covariant returns
## Discussion
### Unconstrained type parameter annotation T??
You can only use `T?` when is constrained to be a reference type or a value type. On the other
hand, you can only use `T??` when `T` is unconstrained.
The question is what to use for the following:
```C#
abstract class A<T>
{
internal abstract void F<U>(ref U?? u) where U : T;
}
class B1 : A<string>
{
internal override void F<U>(ref U?? u) => default; // Is ?? allowed or required?
}
class B2 : A<int>
{
internal override void F<U>(ref U?? u) => default; // Is ?? allowed or required?
}
class B3 : A<int?>
{
internal override void F<U>(ref U?? u) => default; // Is ?? allowed or required?
}
```
Our understanding is that this would be:
```C#
abstract class A<T>
{
internal abstract void F<U>(ref U?? u) where U : T;
}
class B1 : A<string>
{
internal override void F<U>(ref U?? u) => default;
// We think the correct annotation is
// void F<U>(ref U? u) where U : class
// because the type parameter is no longer unconstrained. The `where U : class`
// constraint is required, as U? would otherwise mean U : struct
}
// We may want to allow U?? even in the above case, so
class B1 : A<string>
{
internal override void F<U>(ref U?? u) => default; // allowed?
// This would not require the `where U : class` constraint because `U??` cannot
// be confused with `Nullable<U>`
}
class B2 : A<int>
{
internal override void F<U>(ref U?? u) => default;
// The correct annotation is
// void F<U>(ref U u)
// We could allow
// void F<U>(ref U?? u)
// although it would be redundant
}
class B3 : A<int?>
{
internal override void F<U>(ref U?? u) => default;
// The correct annotation is
// void F<U>(ref U u)
// We could allow
// void F<U>(ref U?? u)
// although it would be redundant
```
In the above, we're wondering whether we should allow `??` without warning even in cases where
there's existing syntax to represent the semantics. One benefit is that you could write
```C#
abstract class A<T>
{
internal abstract F<U>(ref U?? u) where U : T;
}
class B1 : A<string>
{
internal override F<U>(ref U?? u) { }
}
```
Since the override cannot be confused with `U?`, there's no need for the `where U : class`. On the
other hand, the benefit seems marginal and it's not needed to represent the semantics. It could
also be added later.
**Conclusion**
We like the syntax `??` to represent the "maybe default" semantics. We think that `??` should be
allowed in the cases where we have other syntax to represent the semantics. A warning will be
provided and hopefully a code fix to move the code to the "more canonical" form. The syntax `??`
is only legal on type parameters in a nullable-enabled context.
We considered using the `?` syntax the represent the same semantics, but ruled it out for a couple reasons:
1. It's technically difficult to achieve. There are two technical limitations in the compiler.
The first is design history where a type parameter ending in `?` is assumed to be a struct. This
has been true in the compiler all the way until nullable reference types in the last release. The
second problem is that many pieces of C# binding are very sensitive to being asked if a type is a
value or reference type and asking the question can often lead to cycles if asked before the answer
is absolutely necessary. However, `T?` means different things if `T` is a struct or unconstrained, so
finding the safe place to ask is difficult.
2. Unresolved design problems. In overriding `T?` means `where T : struct`, going back to the beginning of
`Nullable<T>`. This already caused problems with `T? where T : class` in C# 8, which is why we introduced
a feature where you could specify `T? where T : class` in an override, contrary to past C# design where
constraints are not allowed to be re-specified in overrides. To accommodate overloads for unconstrained
`T?` we would have to introduce a new type of explicit constraint meaning *unconstrained*. We don't have
a syntax for that, and don't particularly want one.
3. Confusion with a different feature. If we use the `T?` syntax, the following would be legal:
```C#
class C
{
public T? M<T>() where T : notnull { ... }
}
```
what you may think is that `M` returns a nullable reference type if `T` is a reference type, and a nullable
value type if `T` is a value type (i.e., `Nullable<T>`). However, that's *not* what this feature is. This
feature is essentially *maybe default*, meaning that `T?` may contain the value `default(T)`. For a reference
type this would be `null`, but for a value type this would be the zero value, not `null`.
Moreover, this seems like a useful feature, that would be ruled out if we used the syntax for something else.
Consider a method similar to `FirstOrDefault`, `FirstOrNull`:
```C#
public static T? FirstOrNull<T>(this IEnumerable<T> e) where T : notnull
```
The benefit is that there is a single sentinel value, `null`, and that the full set of values in a struct
could be represented. In `FirstOrDefault`, if the input is `IEnumerable<int>` there is no way to distinguish
a non-empty enumerable with first value of `0`, or an empty enumerable. With `FirstOrNull` you can distinguish
these cases in a single call, as long as `null` is not a valid value in the type.
Due to CLR restrictions it is not possible to implement this feature in the obvious way, so this feature may
never be implemented, but we would like to prevent confusion and keep the syntax available in case we ever
figure out how we'd like to implement it.
### Covariant returns
We looked at the proposal in #2844.
There's a proposal that for the following
```C#
interface I1
{
object M();
}
interface I2 : I1
{
string M() => null;
}
```
`I2.M` would be illegal as it is, because this is an interface *implementation*, not an
*override*. Interface implementation would not change return types, while overriding would. Thus
we would allow
```C#
interface I1
{
object M();
}
interface I2 : I1
{
override string M() => null;
}
```
which would allow the return type to change. However, it would override *all* `M`s, since
explicit implementation is not allowed.
**Conclusion**
We like it in principle and would like to move forward. There may be some details around
interface implementation vs. overriding to work out.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,123 +0,0 @@
# C# Language Design Meeting for Feb. 26, 2020
## Agenda
Design Review
## Discussion
Today is a design review, where we collect the design team, selected emeritus
members, and a number of broad ecosystem experts to provide some "in-the-moment"
feedback to our current designs and their directions.
Today we presented more of our thoughts on the top-level statements/"simple programs"
features and records.
### Simple Programs
We have a prototype of simple programs. As per the existing spec, you can have
top-level functions among all files, and other statements in a single file.
Collected feedback, not in any particular order:
* Supporting local functions in files other than the top-level statement file doesn't
seem useful and could cause confusion. If there's a local function defined in a file
only with classes, it would appear that that function would be in scope for the
classes, according to C# lexical scoping conventions. However, since these are defined
as *local functions*, not top-level methods, it would be an error to use them inside
the class. Moreover, even if that confusion is resolved, there doesn't seem to be a
compelling reason to allow it in the first place. Because these functions are not
accessible from anything except the main statement file, it would be most likely to
want to put the functions in that file, next to the uses. The only benefit from allowing
local functions in separate file may be as a helper file that is linked into other
compilations. However, wrapping these utility functions in a class so they can be used
in more places seems like a small burden with big benefits. Once wrapped in a class,
these functions are simply methods like in C# today.
* Mixing classes and statements in the same file could generate some confusion. The existing
design is that classes can see the variables created by statements, but it would be illegal
to reference them. This keeps the option open to allow access later. However, this could be
a confusing middle ground. To simplify the situation we could require only statements in the
top-level in one file (forcing all types to be declared in separate files). However, there is
interest in using utility classes in the top-level statements, perhaps especially with a
forthcoming records feature that provides simple, short syntax for declaring new types.
* Many of these features mirror what we already have in CSX. It's good that our
current designs are similar and allow these constructs in more places, but since the semantics of
this design have subtle differences from CSX this would effectively create a third dialect of C#.
There's some desire to unify these worlds, but it's difficult. CSX is designed to allow all
values to be persisted, which is important for the scripting "submission" system, but this makes
a number of types of statements illegal that we have support for in the current design, like
ref locals. It also creates a burden for new designs, where statements need to be explicitly
designed for both CSX and C#. For example, the new `using var` declaration form is nonsensical
under the CSX design and probably should be illegal. Since the current 'simple programs' design
effectively treats statements like they are part of a method body, there's a cleaner semantic
parallel with C#, meaning less special-casing.
### Records
Here we presented a variety of different pieces of designs we have been thinking about.
#### Nominal Record
Feedback:
* One of the biggest drawbacks of the current writeable-property style in C#, where types are
declared with public mutable properties that are then initialized using object initializers,
is that author can't enforce that certain properties are always initialized. It would be a
big disappointment if any "nominal records" feature that we built couldn't support this feature.
* With the design as-is there's no way to validate the whole state of the object. However, that's
also true of the object-initializer style currently in use, and this doesn't seem to be as a big
of a problem for current users.
#### Value Equality
* Positive feelings on generating `.Equals(T)` and implementing `IEquatable<T>`, mixed feelings
on generating the `==` and `!=` operators.
* If we opt-in the whole class using `value class`, we also need an opt-out for individual members.
Regular classes also often have somewhat specialized equality requirements, like wanting to compare
certain lists as sequence-equal, or compare strings ignoring case. This observation points to a
lot more customization points for value equality on general classes than value equality on records.
* Using value equality on mutable state is seems dangerous if the type is used in a dictionary,
but despite the danger, other languages (Java, Go) have value equality for common types, like
lists, that can be easily added to a dictionary.
* We don't currently have a robust mental model for what it means to be a "value class." Is "value
class" a separable concept from "implementing value equality," which people often do today? Or
is it not a different type of class, but simply a modifier signaling an implementation detail,
namely that the compiler generates value equality automatically. If we think of value equality
as a public contract, how does that change our view of existing code? Classes can currently
override Equals, but we don't distinguish what *kind* of Equals they provide. That isn't a
language concept, in a sense, but a part of the documentation.
### Nominal Records
* When using the `with` expression on nominal records, the generated parameter-less `With` method
looks a lot like `Clone`. It does little aside from return a new object with a shallow copy of
the state. If `With` is essentially Clone, why not use one of the existing forms of Clone that we
already have?
* ICloneable is deprecated and MemberwiseClone is protected. Maybe we should just call the method
Clone(), but not override or implement any of the other framework uses.
* This feature looks a lot like structural typing from other languages, like Javascript's "spread"
operator, but that is not the feature we're currently trying to build. This is feature is still
about declaring new types, not providing some compatibility between existing types.
* We spent a lot of time talking about validation and "validators", a very recent concept that was
floated as an alternative to constructors, executing after the `with` expression.
* There's some general concern about having no capability of validation, but no consensus on
exactly how validators should work.
* If validators work against the copied fields of the object, that seems to imply that the
fields are the state being operated on. On the other hand, only certain members can be
mentioned in the `with` expression. Why wouldn't those be the things that are copied? Instead
of all the state?

View file

@ -1,80 +0,0 @@
# C# Language Design for March 9, 2020
## Agenda
1. Design review
2. Records
## Discussion
### Simple Programs
In the design review we covered the "simple programs" feature. A big piece of feedback is that
they didn't like the "middle ground" we had carved out with local functions. Right now we have
local functions that can exist both in the top-level statement "file" and also across other
files. Because the design allows only local functions at the top level, those functions are only
accessible from and can only access the statements in the "top-level statement file."
The feedback was that this would be an unfortunate design point. Organizing local functions in
other files, when they can't be accessed by other files, is a big problem. There are two places
we could go with this. We could either pull back and allow many of these forms only in the
"top-level statement file." Or we could go the other way and allow more functionality at the top
level, like allowing truly top-level members, including functions and variables, that can be
declared and referenced from everything in the program.
Allowing full top-level members is attractive but opens a lot of questions. The most important is
top-level variables. By allowing top-level variables and giving them the C# default of
mutability, we would effectively be enabling mutable global variables. There is collective
agreement that in everything but the smallest programs this is a bad programming practice and we
shouldn't encourage it.
We could try to pivot on syntactic differences. One major difference between local variables and
functions and member-level variables and functions is that members allow accessibility modifiers.
Top-level statements could be, by default, local variables. Accessibility modifiers, `public`,
`private`, `internal`, et al., could differentiate between top-level and local variables.
However, this feels like it may be too subtle a design point, relying too much on minor syntactic
decisions to decide things like scoping.
The biggest takeaway is that this is a complex topic with a lot possibilities. Maybe we could try
to carve out a small portion to make some progress, without committing to a narrow design for the
entire space of "top-level members." We generally like this approach, but CSX makes things
difficult. Since the existing design focuses on local variables and local functions,
compatibility with any sort of submission system is a problem. Fundamentally, we would need to
decide which pieces of state in a C# program are "transient," in that they cannot be referenced
from a new submission, and which are "preserved."
The value still seems important enough to move forward. There's general agreement that top-level
statements are useful. Some people think they are useful in the simple form already presented,
while other people want to see this as the starting point for a full feature, and these views are
roughly compatible.
If we move forward with our subset, we need to flesh out the mental model for how it works.
Specifically, it's important to note why top-level variables and functions would be inaccessible
from inside classes. One way to think about this is the difference between instance and static.
When you're in a static context, all the instance variables are visible, but not accessible. You
could also model it as the statements are directly inserted into Main (which is true).
**Conclusion**
We'd like to move forward with the prototype with the modification that top-level statements can
only appear in one file. Symbols declared in these statements would be visible, but an error to
access inside classes. All the top-level statements are treated as if they were inside an async
Main method.
### Value equality
When we discussed records in the design meeting we brought up value equality for records and a
proposal for extending to regular classes. A big shift was that records should have an easy
global automatic value equality, while general classes should never have a global opt-in.
This seems contradictory, but if records have a set of default semantics that naturally fit value
equality, then having it enabled by default is suitable. Value equality plays particularly well
with immutability. Since records strongly support immutable programming, supporting value
equality is natural. For arbitrary classes, however, it's not clear at all how value equality
should behave. Opt-ing in either all or only some members seems to have downsides for many class
examples.
We're not ruling out value equality for regular classes, but for the future we'd like to examine
specifically how we'd like value equality to work for records. This could impact how and when we
bring generated value equality to conventional user classes.

View file

@ -1,103 +0,0 @@
# C# Language Design Meeting for March 23rd, 2020
## Agenda
1. Triage
2. Builder-based records
## Discussion
### Triage
#### Generic constraint `where T : ref struct`
Proposal: #1148
This is a very complicated area. It's probably not good enough to add this generic constraint,
because things like default interface methods create a boxed `this` parameter. It's likely that
we would need runtime support to make this safe.
This is somwewhat related to the designs in the shapes/roles proposal in that it's about using
the "shape" of an interface, possibly with more restrictions than interfaces themselves. Since
both proposals may require runtime changes it would be valuable to batch up those changes
together.
##### Conclusion
Push to at least C# 10.0. Should be considered in concert with the shapes/roles proposals.
#### Improve overload resolution for delegate compatibility
Proposal: #3277
This is a parallel to changes we previously made to betterness where we remove overloads
that will later be considered invalid, to be removed from the overload resolution candidate
list in the beginning. This functionally will cause more overload resolution calls to succeed,
since invalid candidates will be removed and this will prevent overload resolution ambiguitiy.
##### Conclusion
Tentatively added to C# 9.0. We'd like this proposal for function pointers, so if we were to
implement this for function pointers and correct overload resolution for delegates at the same
time, that would be desirable.
#### Allow GetEnumerator from extension methods
Proposal: #600
First thing is to confirm that there's no compat concern. When we tried to extend the behavior
for `IDisposable` we ran into a problem because `foreach` *conditionally* disposes things which
match the `IDisposable` pattern. This means that if anything starts satisfying the pattern which
didn't before, an existing method may be newly called. If we ever conditionally use the `foreach`
pattern this would probably also be a breaking change.
##### Conclusion
Willing to accept it any time, as long as we confirm that it's not a breaking change.
### Builder-based records
When discussing records we've had various
designs that focus on "nominal" scenarios, where the members of the record are set by name, instead of lowering into method parameters. One proposed implementation strategy is a new series of rules around initialization that we've sometimes called "initonly." We've also looked at using struct "builders" in the past for a similar purpose, and would like to revisit some of these discussions.
We have a proposal from a community member, @HaloFour, that lays out another example implementation strategy that we're using for discussion.
https://gist.github.com/HaloFour/bccd57c5e4f3261862e04404ce45909e
There are certainly some advantages to this structure:
* Uses existing valid C# to implement the pattern, making it compatible with existing compilers. If
we avoid usage of features like `ref struct` and `in`, it could be compatible for even older
consumers, since this would be binary compatible with C# 2.0 metadata.
* Allows the type being built to always see the whole set of property values being initialized,
meaning that the author of the type can validate the new state of the object.
* No new runtime support for any features (e.g., does not require covariant returns)
There are also some disadvantages.
Performance could be a problem. For classes, the biggest concern is stack size. Currently,
initializing a class with an object initializer only requires a single pointer on the class and
then each member is initialized separately, the initializer values don't all need to be on the
stack simultaneously. If we use a struct builder, the entire builder needs to be on the stack
before initialization.
For structs, there is the extra stack space cost, but having a builder also effectively doubles
the metadata size of the every struct. It's also potentially harder for the CLR to optimize the
initialization and remove dead stores through the double-struct passing. If we go forward with
this approach we should consult the JIT for their perspective.
We're also not sure that this approach fully eliminates all brittleness in adding/changing fields
and properties across inheritance, especially if the inheritance is split across assemblies and
only one author is recompiled, or they are recompiled in a different order. If we can find a way
to close those holes, or limit the feature to prevent these situations, that could be an
important mitigating factor.
#### Conclusion
There's a difficult balance here. Some members are focused on about performance, some prioritize
ecosystem compat, and others prioritize "cleanliness" of design, in different directions. Almost
everyone has a different priority and prefers different approaches for different reasons. We need
to discuss things more and reduce some of the unknowns.

View file

@ -1,117 +0,0 @@
# C# Language Design Meeting for March 25, 2020
## Agenda
1. Questions around the new `nint` type
2. Target-typed new
## Discussion
Issue #3259
### LangVersion
THe question is what the behavior of the compiler should be when seeing an `nint`
type in `langversion` C# 8. Our convention is that the compiler never preserves
old *behavior* for older language versions. For instance, we do not preserve the
code for older code generation strategies and switch to that with the language
version flag. Instead, `langversion` is meant to be guard rails, providing
diagnostics when features are used that aren't available in older versions of the
language.
There are a few options we could take.
1. Make an exception for `nint`, allowing them to be seen and compiled like an
`IntPtr` in `langversion` C# 8.
2. Make a wider divergence between `nint` and `IntPtr`. Adding a `modreq` to
the emitted `IntPtr` type would make them effectively unusable by older language
versions and other languages.
3. Preserve the behavior, as long as no new semantics are used. For instance,
using the arithmetic operators on `nint` and on `IntPtr` have different semantics.
It would be an error to use any of these operators in older language versions.
**Conclusion**
We think (3) is the best balance.
### `IntPtr` and `nint` operators
We have two proposals:
1. Remove built-in identity conversions between native integers and underlying types and add explicit conversions.
2. Remove `nint` operators when using the `IntPtr` type
**Conclusion**
(1) is a little too harsh. Let's do (2).
### Behavior of constant folding
The concern is platform dependence.
In the following example
```C#
const nint m = int.MaxValue;
const nint u1 = unchecked(m + 1);
nint u2 = unchecked(m + 1);
```
if the machine is 32-bit, then the result overflows. If the machine is 64-bit, it does not.
While it's possible in the existing language to produce constant-folded values which are
undefined, we don't think that behavior is desirable for nint.
The main contention is what to do in a `checked` context if we know the value will overflow
32-bits. We could either produce an error, saying that this will overflow on some platforms,
or produce a warning and push the calculation to runtime, warning that the calculation may
overflow at runtime (and produce an exception).
**Conclusion**
Whenever we can safely produce a constant value under 32-bits, we do constant folding. Otherwise,
the result is non-constant, and under `checked`, the code produces a warning and the result
is non-constant.
### Interfaces on `nint`?
Should interfaces on `IntPtr` and `nint` match? Or should `nint` only accept a certain set of
compiler-validated interfaces on `IntPtr`?
**Conclusion**
We trust that interfaces will only be added to `IntPtr` with recognition that those interfaces
also affect `nint`. We'll make all interfaces on `IntPtr` available on `nint`, with `IntPtr`
occurrences substituted for `nint`.
## Target-typed `new`
https://github.com/dotnet/csharplang/blob/master/proposals/target-typed-new.md
Clarification about library evolution: if a user uses `new()`, adding a constructor to a type
can produce an ambiguity. Similarly, if a method is called with `new()` that can produce an
ambiguity if more overloads of that method is added. This is analogous with `null` or `default`,
which can convert to many different types and can produce ambiguity.
The spec currently specifies that there are a list of types where target-typed new is allowed. To
simplify, we propose that we specify that target-typed new should produce a fully-typed `new` and
the legality of that expression is defined elsewhere. This does make `new()` work on enums, which
is currently proposed as illegal because it may be confusing. However, `new Enum()` is legal
today, so we think that it should be allowed for target-typed `new` simply because of
consistency.
There's some debate on what it should do for nullable value types. On the one hand, the rule
"new() is just shorthand for writing out the type on the left," implies that the result should be
`null`. On the other hand, the nullable lifting rules would imply that the base type of the
target should be the underlying type, not the nullable type. Overall, we think that `new`ing the
underlying type makes the most sense, both because it's the most useful (we already have a
shorthand for `null`) and because it's likely what the user intended.
For `dynamic`, we will not permit it simply because `new dynamic()` is also illegal.
Final thought: many thanks to @alrz for the great contribution!

View file

@ -1,112 +0,0 @@
# C# Language Design Meeting for March 30, 2020
## Agenda
Records
1. Builders vs init-only
2. Possible conflicts with discriminated unions
3. Value equality
## Discussion
### Builders vs. init-only
We discussed more of the tradeoffs of using builders vs using an "init-only" feature for records,
and looked into requirements for other languages, including VB and F#. Based on our current
designs, the work needed to consume the new features for "init-only" seem fairly small.
Recognizing the `modreq` and allowing access to init-only members, and calling any necessary
"validator" on construction, are pretty simple features for VB. VB already has the syntactic
requirements for the feature (object initializers), and we'd like to keep it possible for VB to
consume major changes in the API surface, if not write those new features. F# is undergoing
active development and changes are certainly viable there. Because the scope of changes is more
open-ended in F#, it's possible it could feature more implementation work.
Notably, most of the features associated with "init-only" and "validators" do not require a new
runtime, only a new compiler version. The new compiler version is necessary to recognize safety
rules (validators must always be called after constructors, if they exist), but they don't use
any new features in the CLR. The only feature potentially requiring runtime features is
overriding a record with another record, which could potentially require covariant returns.
The remaining differences seem to come down to whether you can "see" the whole object during
initial construction (as opposed to validation). If you can see the whole object immediately,
that makes writing a `With` method that avoids a copy if all the values are identical very
straightforward. However, this could be done for "init-only" as well, by moving this semantic to
the `with` expression, optionally comparing the arguments to the `With` expression with the
values on the receiver object and avoiding calling With if they are identical. Therefore we don't
think we're actually ruling anything out by going down the "init-only" route.
There are advantages in going down the "init-only" route instead. The performance for structs
is probably better and more optimizable, and the IL pattern seems clearer and less bloated.
**Conclusion**
We like the "init-only" IL better. Given the path forward for other languages and compatibility
with many runtimes, we think it's a better future as an IL pattern.
### Conflicts with discriminated unions
There was a general question if these decisions could impact a future discriminated unions feature.
We don't have a lot in mind, but if we do end up building a discriminated union made of the records
feature, there is one component we may want to reserve. Discriminated unions often have a set of simple
data members. For instance, if we wrote a Color enum as a discriminated union, it could look like.
```C#
enum class Color
{
Red,
Green,
Blue
}
```
If we reduce these to records, it may look like:
```C#
abstract class Color
{
public class Red() : Color;
public class Green() : Color;
public class Blue() : Color;
}
```
The problem is: since those classes are effectively singletons, we'd like for the instances to be
cached by the compiler, to avoid allocations. However, we don't currently have a syntax in C# that
means "singleton." Scala uses the "empty record" syntax to mean singleton. We need to decide if we'd
like to reserve that syntax ourselves, or find some other solution.
### Value equality
We've decided that we want value equality by default for records. We need to settle on what that
means. The primary proposal on the table is shallow-field-equality, namely comparing all fields
in the type via EqualityComparer. This would match the semantic we have decided on for "Withers,"
where the shallow state of the object is copied, similar to MemberwiseClone.
There's a large segment of the LDM that thinks doing anything except for field comparison is
problematic because it introduces far too many customization points for the record feature.
Almost all custom equality would have to deal with sequence equality and string equality, which
are already very different mechanisms. There's a proposal that we could provide further
customization via source generators, which could allow almost any customization.
A follow-up to that is: why have value equality by default at all? Why not use source generators
for all value equality? One problem with that is that we want the simplest records to be very
short. The other problem is that we effectively have to pick an equality (C# will inherit one if
you don't). We previously decided that value equality is a non-controversial default -- if it's
wrong it's probably not worse than when reference equality is wrong, and it's often better.
Struct-like field-based equality is a simple and familiar version of value equality.
We also need to decide on value-equality as a separable feature for regular classes. Some people
like the idea of a separate feature and would be fine even with the constrained version that only
compares fields. Others don't think this feature meets the hurdles for a new language feature. If
we don't have customization options, it may be rarely used, and it seems possible that a source
generator version could be the much more popular version. It's also worth noting that, as a
separable feature, we don't need to add separable equality now.
**Conclusion**
No separable value equality for non-records, right now. Default record equality is defined as
field-based shallow equality.

View file

@ -1,119 +0,0 @@
# C# Language Design Meeting for April 1, 2020
## Agenda
1. Function pointer calling conventions
2. `field` keyword in properties
## Discussion
### Function Pointers
There are a few open issues and proposals for generalization of function pointers based on
new runtime features.
https://github.com/dotnet/csharplang/issues/3324
#### NativeCallableAttribute
The attribute already exists in the CLR, and allows for specifying a calling convention other than
`managed` (which means that it can't be called from C#, but could be used from function pointers).
The question is what level of support we want to provide in the language for this attribute.
Since the runtime behavior is to crash if the method is called incorrectly (meaning, invoked at
all from C# if not through a function pointer), we almost certainly want to recognize the
attribute's existence and provide errors for incorrect usage.
We considered more restrictions than the ones mentioned in the issue (only usable in function
pointers, parameters must be blittable, must be static) like restricted accessibility or special
syntax. The consensus is that is too much work for a small feature.
**Conclusion**
`NativeCallableAttribute` should be recognized and the restrictions are accepted, with the
addition that generics are also prohibited in the method and all containing types, recursively,
and delegate conversion is also prohibited.
#### Supporting extra calling conventions
The existing proposal mandates that the only the existing calling conventions are supported. We
previously said we'd consider new calling conventions when they were proposed by the runtime. We now
have proposals about some likely new calling convention from the runtime.
The proposal is that the "calling convention" syntax in function pointers could be a general identifier, and legal values for the runtime would be determined by name matching against type names
starting with `CallConv` in a particular namespace, and passing through Unicode lower-case mapping.
**Conclusion**
Accepted.
#### Attributes on function pointer types
The syntax is getting a bit verbose, but allowing an extra axis for customization seems like the
simplest extension of function pointers that provides the level support that the runtime may need
in the future.
Currently our favored syntax is:
```C#
delegate* cdecl[SuppressGCTransition, MyFuncAttr]<void> ptr;
```
The attribute-like syntax would be turned into the `modreq`s on the function pointer type that
would be used by the runtime to encode special calling behavior. These would also effectively be
different types at the C# level and would not have implicit conversions between them.
**Conclusion**
Accepted, assuming there are no problems in implementation.
### `field` keyword in properties
Over the years there have been many requests for similar features, e.g.
https://github.com/dotnet/csharplang/issues/140
Maybe the simplest version is that there is a contextual identifier, e.g. `field`, which refers
to an implicit backing field of the property. This seems useful, but a big limitation is that the
backing field of the property must have the same type as the property. If lazy initialization is
a common case, that seems likely to require differing property types, as the backing field would
often be nullable, but the initialized field would be not nullable.
On the other hand, biting off too much in a single feature may delay simple scenarios
unnecessarily. Should we try to address the simplest scenarios first, and leave more complex
scenarios for later? In this case we probably need to find what scenarios are served by that
design. Two things that are recognized are simple validation, e.g.
```C#
public int PositiveValue
{
get => field;
set
{
if (value < 0)
throw new ArgumentException("Cannot be negative")
field = value;
}
}
```
and registration like `INotifyPropertyChanged`
```C#
public int P
{
get => field;
set
{
PropertyChanged();
field = value;
}
}
```
Let's confirm that these scenarios are the ones most commonly requested and that they aren't
addressed or modified by any of the other scheduled language features, e.g. source generators for
INotifyPropertyChanged. From there we can discuss the specific proposal with a better
understanding of the problem and solution space.

View file

@ -1,86 +0,0 @@
# C# Language Design Notes for April 6th, 2020
## Agenda
Init-only members
## Discussion
We have a proposal to dive into: https://github.com/jaredpar/csharplang/blob/init/proposals/init.md
* The proposal notes that you can set `init` fields of the base type during construction, similar
to `readonly`. This is not how `readonly` works today, only the declaring type can set readonly
fields
* The proposal allows `init` on class and struct declarations as a shorthand for `init` on types.
This is different from how `readonly` struct works today, where there is no syntactic shorthand,
`readonly` simply adds the additional requirement that all instance fields are marked `readonly`.
* For the runtime: does this feature prohibit runtime restrictions on setting `readonly` instance
fields in the future? Put simply: yes. To avoid breaking C#, the runtime would be required to
either respect the proposed `InitOnlyAttribute`, or restrict optimizations to not alter the code
for these features.
* Use in interfaces: the proposal prohibits it, but the following example seems useful:
```C#
interface I
{
int Prop { get; init set; }
}
public void MyMethod<T>() where T : I, new()
{
var t = new T() {
Prop = 1
};
}
```
* Signature compatibility: should `init` properties be compatible with mutable ones? That is, should
removing `init` in favor of a bare `set` be binary-compatible? This impacts our decisions for how
we think about safety in older compilers:
* If we use a modreq to prevent other compilers from unsafely using a `setter`, that affects the
signature of the method, and would make the above a breaking change
* If we want accept that older compilers are not a problem (C#, VB, and F# will all be updated),
perhaps we don't need to specially guard this at all
* We could use attributes to mark *and* guard, by using the `Obsolete` attribute. `ref struct`s
use this guard by having an Obsolete attribute with a reserved message, that is ignored by
compatible compilers.
### Accessors
Should we allow three accessors: `get, set, init`? A big problem here is that you can end up
calling instance members in a constructor that invoke the setter, not the initter, for a
property. This means that there are few, if any, invariants that hold for init vs. set, and
weakens the feature significantly.
### Syntax
`init` vs. `initonly` for syntax. On the one hand, `init` is inconsistent with `readonly` (vs. `initonly`), but on the other hand we're pretty sad that `readonly` is such a long keyword. It also has few analogies
with properties, where `readonly` isn't allowed at all, and the shorter `init` keyword seems more similar to
`get` and `set`
* Usage of `init` as a modifier: there are a number of different meanings here, and similarities
between `readonly` and `init` is separating the more places we use it. For instance, if `init` is
allowed as a member modifier, it seems similar to `readonly` on members, but they actually do
different things. Moreover, `readonly` on types means that all instance fields and methods are
`readonly`, while `init` on types would only mean `init` on fields, not on methods.
* What are the uses for `init` methods, aside from helper methods? Could they be used in collection initializers?
### Required Initialization
The proposal in this doc is that required initialization is also an important feature for setters. We're
going to leave that discussion for a future meeting. As proposed, nullable warnings are not modified for
`init` members.
**Conclusions**
No `init` on types. No agreement on syntax. We probably have to talk about more of the use cases.
We've decided that having three different accessors is not helpful. Settled that we will place a
`modreq` on all `init` setters.

View file

@ -1,154 +0,0 @@
# C# Language Design for April 8, 2020
## Agenda
1. `e is dynamic` pure null check
2. Target typing `?:`
3. Inferred type of an `or` pattern
4. Module initializers
## Discussion
### `e is dynamic` pure null check
We warn that doing `e is dynamic` is equivalent to `e is object`, but `e is object` is a pure
null check, while `e is dynamic` is not. Should we make `is dynamic` a pure null check for
consistency?
**Conclusion**
Yes.
### Target-typing `?:`
The simplest example where we have a breaking change is
```C#
void M(short)
void M(long)
M(b ? 1 : 2)
```
Previously this would choose `long`, because the expressions are effectively typed separately,
and when inferring `1 : 2` we would choose `int`, and then rule out `short` during overload
resolution as invalid.
Target-typing converts each arm in turn to find the best possible type, selecting `short` instead
of `long`. That means the first overload is selected instead with target-typing.
It's hard to easily avoid this breaking change. The obvious mechanism, running overload
resolution twice, is problematic because having multiple arguments with `conditional` expressions
produces exponential growth in the number of passes of overload resolution.
**Conclusion**
Let's talk about this again in a separate meeting. Since whatever we choose here will probably
be the final decision (any further changes will be breaking), we want to be sure we're making the
best choice.
### Inferred type of an `or` pattern is the common type of two inferred types
```C#
object o = 1;
if (o is (1 or 3L) and var x)
// what is the type of `x`?
```
The problem here is that the existing common type algorithm allows conversion that are forbidden
in pattern matching. In the above case we would choose `long`, because `3L` is a long, and `1`
can be converted to `long`. However, in pattern matching the widening conversion from `int` to
`long` is illegal.
The proposal is to narrow the set of conversions only to implicit reference conversions or
boxing conversions. Why this could be useful is the example
```C#
class B { }
class C : B { }
if (o is (B or C) and var x)
// x is `B`
```
A follow-up question is about ordering. The proposed rules only infer types mentioned in
the checks, meaning that the following
```C#
if (o is (Derived1 or Derived2 or Base { ... }) and var x)
```
looks like this today
```C#
((Derived1 or Derived2) or Base)
-> ((object) or Base)
-> (object)
```
On the other hand, if the example were parameterized in the opposite way,
```C#
(Derived1 or (Derived2 or Base))
-> (Derived1 or (Base))
-> (Base)
```
So the ordering seemingly matters if the `or` pattern is a simple binary expression.
The first question is if
```C#
if (o is (Derived1 or Derived2 or Base { ... }))
```
should produce `Base`, and the second question is if parenthesizing affects this, e.g. explicitly saying
```C#
if (o is ((Derived1 or Derived2) or Base { ... }))
```
produces `object`.
**Conclusion**
We're not seeing a lot of scenarios that depend on these features, but not doing it feels like
leaving information on the table. Functionally, the compiler can infer a stronger type, so why
not do so? The proposed modified common type algorithm is accepted.
We also think that type narrowing for the `or` operation should use all the `or` operations
in a series as the arguments to the common type algorithm.
### Module Initializers
Proposal: https://github.com/RikkiGibson/csharplang/blob/module-initializers/proposals/module-initializers.md
There are a few places where module initializers today. The most common is a shared resource
that is used by multiple types, but the program would like to initialize the shared resource
before the static constructors of the types are run.
The question for the implementation is to how to indicate where the code for the module
initializer will go, and how the compiler will recognize it.
The proposal provides a mechanism to identify the module initializer via an attribute on the module
that points to a type, and type contains a static constructor that acts as the module initializer.
From a conceptual level, this seems more complicated than necessary. Can we put the attribute on the
type or, even the method, that holds the code for the module initializer?
If we go that route, why require the code be in the static constructor at all? Can any static method
be a target? One reason why we may prefer a static constructor is that if the emitted module initializer
calls the target static constructor method, it's easy to insert code before that method is invoked
by writing a static constructor for the containing type. On the other hand, even static constructor
can have code inserted before them, for example with static field initializers.
The last question is whether to allow only one module initializer method, or allow multiple and specify
that the compiler will call them in a stable, but implementation-defined order.
**Conclusion**
Let's let any static method be a module initializer, and mark that method using a well-known attribute.
We'll also allow multiple module initializer methods, and they will each be called in a reserved, but
deterministic order.

View file

@ -1,77 +0,0 @@
# C# Language Design for April 13, 2020
## Agenda
1. Roadmap for records
2. Init-only properties
# Roadmap for records
We want to break down the records feature in a way that gets incremental steps out to partner teams and users sooner, and lets us iterate based on feedback. So what order should we do things in?
Two demanding aspects of records that are certainly important but can probably be done later are:
1. Primary constructors (allowing positional parameters directly on record types)
2. Inheritance to and from record types
So a proposal is to split those off in the first iteration of implementation.
## Decision
We do need to get those right to ultimately ship the feature! However, we want to start by building a version of records that:
* Is nominal only (no primary constructor)
* Inherits from object and can't be inherited from
* Special-cases `with` expressions to `With` methods rather than rely on a "Factory" feature
* Fully implements value equality
For this to be useful we need the init-only property feature at the same time as well, so that there is *some* way to create and initialize immutable records.
In subsequent iterations we will
* Add support for inheritance to records
* Add primary constructors to records
* Generalize the `With` implementation to use factories
* Decide on and embrace defaults (`public` by default?) and abbreviations
On parallel tracks we will work on:
* Factory methods
* Validators?
* Mandatory properties?
* Primary constructors as a general feature?
# Init-only members
Init-only properties are the most urgent separate feature to nail down, as the experience of even the first iteration of records depends on them. There are a couple of design decisions still left open; we address them here.
## Should we have init-only fields?
Readonly fields today can only be assigned during construction, and only through a `this` access within the body of the class that declares the field. As we extend the concept of "initialization time" to cover execution of init-only setters (by object initializers in client code) as well as validators (if we add those to the language), it makes sense that `readonly` fields should be assignable from within those kinds of members as well.
We would *not* allow readonly fields to be directly assigned in an object initializer, however, as that would undermine the expectation that the class author has full control over how and when they are assigned.
Allowing init-only properties to assign readonly fields would address most of the init-only scenario: An object initializer can be used to set immutable state on the newly created object. A dedicated init-only form of fields is not needed for that. It probably *does* have valid scenarios (in programming styles where immutable fields are themselves public), but those seem less central. We could wait see if that rises to the importance of a seperate, subsequent feature.
### Decision
Let's allow init-only property setters (and validators, if and when we add them) to assign to `readonly` fields of the same object.
Let's not add init-only fields now. We can consider a new kind of field that can be initialized directly in object initializers later, as a separate feature, if we become convinced that it's worthwhile.
## Syntax
Should the setters of init-only properties be called `init` or `init set`? In other words, is `init` a modifier on the `set` accessor, or does it replace it completely?
Shorter is generally nicer. However, we do foresee a (near?) future where `init` as a modifier could be applied to other members and accessors, to mean that they can only run during initialization (and in return would get privileges such as assigning to readonly members).
### Decision
`init` it is. `init` as a modifier is an interesting feature, but we can discuss it separately. If we do, we can consider allowing `init set` as a long form of an init-only setter for regularity when code has `init` modifiers in multiple places. But for now, let's just allow `init` instead of `set`.
## Init-only indexers
Indexers also have `set` accessors. Should they also be allowed to declare an `init` accessor instead?
### Decision
It makes perfect sense, is somewhat useful, would be more regular in the language, and has straightforward semantics. Let's do it.

View file

@ -1,74 +0,0 @@
# C# Language Design Notes for Apr 15, 2020
## Agenda
1. Non-void and non-private partial methods
2. Top-level programs
# Non-void, non-private partial methods
Proposal: https://github.com/dotnet/csharplang/issues/3301.
Currently, partial methods are required to return void. They are implicitly private, and cannot have an explicit accessibility. In return for that, calls to a partial method that has a *declaration* but no *definition* can be safely elided by the compiler. Thus, partial methods serve as optional "hooks" or points of extension for generated code, code that is conditionally included based on compilation target, etc.
With the expected advent of source generators in the C# 9.0 timeframe, there are likely to many scenarios where these restrictions are too limiting. The proposal suggests a different trade-off for partial methods, where they *can* have return values, and *can* have broader accessibility, but in exchange the definition is *mandatory*: An implementation *must* be provided, since calls can't be elided.
The mandatory aspect can in fact be viewed as a feature. It is a way for one part of the code to *require* another part to be specified, even as they are separated across files and authorship.
Main concern is that we would need to preserve the "old" semantics for compatibility in cases that are already allowed in C#, and developers may accidentally fall into that case, failing to compel another part to produce an implementation, and having calls elided without wanting to.
One mitigating factor is that the existing feature doesn't allow you to explicitly say `private` - it has to be implied. So we could say that if `private` is explicitly supplied we are in the new semantics, and the method implementation is required. It's a subtle an non-obvious distinction, but at least it is there.
Another question is whether we would allow other members to be partial. We would need to work out the syntax in each case: E.g. how do you distinguish a partial property definition from a declaration that implements it as an auto-property?
## Decision
Despite the weirdness of distinguishing between implicit and explicit private (the latter requires an implementation, the former does not), we are ok with accepting this wart in the language. The feature extension is valuable, and alternative solutions are distinctly less appetizing.
On the other hand we are not ready to allow `partial` on other kinds of members. If future scenario bear out a strong need, we will do the design work to hash it out, but we think methods are able to address the vast majority of what's needed.
# Top-level statements
Proposal: https://github.com/dotnet/csharplang/blob/master/proposals/Simple-programs.md
We took a look at the currently implemented semantics to make sure we are happy with them. A couple of questions came up:
## Expressions at the end
Part of the motivation for the feature was to decrease the syntactic distance between C# (.cs) and its scripting dialect (.csx). However, unlike script we still don't allow expressions at the end. For the scripting dialect this is mostly for producing a result in an interactive setting.
### Decision
We are ok with this remaining distance, and would prefer not to have a notion of "expression at the end produces result" in C#.
## Shadow and error
The proposal puts top-level local variables and functions in scope inside type declarations in the program. However, if they are are used in those places, and error is given.
### Decision
This is deliberately there to allow us to do a more general form of top-level functions in the future. We do believe that it protects likely future designs for this.
## Args
Currently there is no way to access the `args` array optionally given as input to an explicit `Main` method. Instead you have to make use of existing APIs that have a slightly different behavior (they include the name of the program as the first element), and certainly look different.
For anyone who uses the APIs in a top level program, they can still trivially move it into an explicit `Main` method at a later point, but going the other way with a `Main` body that uses `args` is not so easy.
There are ideas to:
- add a new API that looks more like `args` (e.g. `Something.Args`) and behaves the same way
- add `args` as a magic variable in top level programs (similar to `value` in property setters), on the assumption that 99.9% of `Main` methods use `args` as the parameter name.
### Decision
We think this is important to pursue further, but aren't going to hold up the feature for it.
## Await triggers a different signature
In the current implementation, the signature of the `Main`-like method generated from the top level program will be different, depending on whether `await` is used or not. If it is, then the signature will include `async Task<...>`, otherwise it won't.
An alternative would be to always generate a `Task`-based signature, and just suppress the usual warning when no `await`s occur in the body. The choice doesn't affect the user much. The main difference is that with the current design there is no need to reference the `Task` types, and any limitations imposed by the language inside async methods are not in force, unless `await` is used.
### Decision
We stick with the current design.

View file

@ -1,64 +0,0 @@
# C# Language Design Meeting for April 20, 2020
## Agenda
Records:
1. Factory methods
## Discussion
The proposal at its core is to allow certain methods to opt-in to certain language semantics
that are only currently valid at construction, namely object initializers, collection
initializers, and the new `with` expression (although that expression is legal on only certain
factory methods).
Possible extension of the feature: allow initializer forms everywhere, but only allow `init`
properties to be initialized when the method is marked with `Factory`. However, almost all uses
of this syntax would be a mutation of the receiver, and it may not be clear that the initializer
syntax produces a mutation.
As to whether `null` should be a valid return: most people think no. Since almost all initializer
forms dereference the receiver, this is essentially creating a very obscure way of producing
exceptions. In addition, all struct values should be permitted, as they are all safe. `default`
should be legal if the target type is known to be a struct. We have not considered what the
behavior should be for unconstrained generics.
There also some concerns about the syntactic extensions. First in that this would make `identifier {
... }` a valid syntactic form in most situations. This may not be syntactically ambiguous today,
but we have a lot of different features, like `block expressions`, which share some syntactic
similarity. Even if there is no syntactic ambiguity, some people are still concerned that the feature
will be too opaque. One way to remedy this would be to require the `new` keyword for this form as well.
So the new syntax would be:
```C#
var s = new Create() { Name = "Andy" };
```
There could be some naming ambiguity here because `Create` could be either a factory method or a
type name. We would have to preserve the interpretation as a type here for compatibility.
There's a broader question of how or if we'd like a general initializer feature. There's some
question of whether the feature is useful enough to deserve the complexity at all, using any
additional syntax. Alternatively, we could embrace the syntax requiring the `new` keyword.
One important piece of history is that initializers are not meant for mutating existing state,
only for mutating new objects. This doesn't necessarily conflict with allowing initializers on
any object, but the reason here is not that the language is suggesting using object initializers
for arbitrary mutation, but that convention alone is good enough to promote use on "new" objects
only.
Regardless of the extensions of the feature, we certainly need to implement something for
records. The core feature requirement here is for the `with` expression, which needs to assign to
`init` fields. We can head two directions: special case the `Clone` method, or build a more general
feature. This is a spectrum, where one end may be a new syntactic form specific to just the Clone
method, and the other end could be a `Factory` attribute that could be applied to any method.
### Conclusion
Right now we're more concerned with what to do for records. In the meantime, let's not support
user-written Clone methods. A Clone method will be generated for a record with an unspeakable type
and the SpecialName flag. The `with` expression will look for exactly that method name. We intend
to decide for C# 9 how that method will be written in source. We'll consider broader `Factory`
scenarios later.

View file

@ -1,180 +0,0 @@
# C# Language Design Meeting for April 27, 2020
## Agenda
1. Records: positional
## Discussion
The starting point for positional records is how it fits in with potential
"primary constructor" syntax. The original proposal for primary constructors
allowed the parameters for primary constructors to be visible inside the class:
```C#
class MyClass(int x, int y)
{
public int P => x + y;
}
```
When referenced, `x` and `y` would be like lambda captures. They would be in
scope, and if they are captured outside of a constructor, a private backing
field would be generated.
One consequence of this design is that the primary constructor must *always*
run. Since the parameters are in scope throughout the entire class, the primary
constructor must run to provide the parameters. The proposed way of resolving
this is to require all user constructors to call the primary constructor, instead
of allowing calls to `base`. The primary constructor itself would be the only
constructor allowed (and required) to call `base`.
This does present a conundrum for positional records. If positional records support
the `with` expression, as we intended for all records, they must generate two constructors:
a primary constructor and a copy constructor. We previously specified that the copy
constructor works off of fields, and is generated as follows
```C#
class C
{
protected C(C other)
{
field1 = other.field1;
...
fieldN = other.fieldN;
}
}
```
This generated code violates the previous rule: it doesn't call the primary constructors.
One way to resolve this would be to change the codegen to delegate to the primary constructor:
```C#
record class Point(int X, int Y)
{
protected Point(Point other) : Point(other.X, other.Y) { }
}
```
This is almost identical, except that the primary constructor may have side-effects, or the
property accessors may have side-effects, if user-defined. We had strong opinions against using
the accessors before because of this -- we couldn't know if the properties were even
auto-properties and whether we were duplicating or even overwriting previous work.
However, we note that violating the rule for our generated code shouldn't be a problem in
practice. Since the new object is a field-wise duplicate of the previous object, if we assume
that the previous object is valid, the new object must be as well. All fields which were
initialized by the primary constructor _must already be initialized_. Thus, for our code
generation it's both correct and safer to keep our old strategy. For user-written constructors
we can require that they call the primary constructor, but because the user owns the type, they
should be able to provide safe codegen. In contrast, because the compiler doesn't know the full
semantics of the user type, we have to be more cautious in our code generation.
This doesn't really contradict with our goal of making a record representable as a regular class.
A mostly-identical version can be constructed via chaining as described above. The only
difference is in property side effects, which the compiler itself cant promise is identical, but
if it were written in source then the user could author their constructor to behave similarly.
Property side-effects have an established history of being flexible in the language and the
tooling. Property pattern matching doesn't define the order in which property side effects are
evaluated, doesn't promise that they even will be evaluated if theyre not necessary to determine
whether the pattern matches, and doesn't promise that the ordering will be stable. Similarly, the
debugger auto-evaluates properties in the watch window, regardless of side effects, and the
default behavior is to step over them when single stepping. The .NET design guidelines also
specifically recommend to not have observable side effects in property evaluation.
We now have a general proposal for both how positional records work, and how primary constructors
work.
Primary constructors work like capturing. The parameters from the primary constructor are visible
in the body of the class. In field initializers and possibly a primary constructor body, they are
non-capturing, namely that use of the parameter does not capture to any fields. Everywhere else
in the class, the parameters are captured and create a private backing field.
Positional records work like primary constructors, except that they also generate public
init-only properties for each positional record parameter, if a member with the same signature
does not already exist. This means that in field initializers and the primary constructor body,
the parameter name is in scope and shadows the properties, while in other methods the parameter
name is not in scope. In addition, the generated constructor will initialize all properties with
the same names as the positional record parameters to the parameter values, unless the
corresponding members are not writeable.
#### Conclusion
The above proposals are accepted. Both positional records and primary constructors are accepted
with the above restrictions. In source, all non-primary constructors in a type with a primary
constructor must call the primary constructor. The generated copy constructor will not be
required to follow this rule, instead doing a field-wise copy. The exact details of the scoping
rules, including whether primary constructors have parameters that are in scope everywhere, or
simply generate a field that is in scope and shadows the parameter, is an open issue.
### Primary constructor bodies and validators
We do have a problem with some syntactic overlap. We previously proposed that our original
syntax for primary constructor bodies could be the syntax for a validator. However, there
are reasons why you may want to write both. For instance, constructors are a good way to
provide default values for init-only properties that may be overwritten later. Validators
are still useful for ensuring that the state of the object is legal after the init-only.
In that case we need two syntaxes that can be composed. The proposal is
```C#
class TypeName(int X, int Y)
{
public TypeName
{
// constructor
}
init
{
// validator
}
}
```
To mirror the keyword used for init-only properties, we could use the `init` keyword
instead. This would also hint that validators aren't *only* for validating the state,
they can also set init-only properties themselves. To that end, we have a tentative name:
final initializers.
#### Conclusion
Accepted. `type-name '{' ... '}'` will refer to a primary-constructor-body and `init '{' ... '}'` is
the new "validator"/"final initializer" syntax. No decisions on semantics.
### Primary constructor base calls
Given that we have accepted the following syntax for primary constructors and primary constructor bodies,
```C#
class TypeName(int X, int Y)
{
public TypeName
{
}
}
```
how should we express the mandatory base call? We have two clear options:
```C#
class TypeName(int X, int Y) : BaseType(X, Y);
class TypeName(int X, int Y) : BaseType
{
public TypeName : base(X, Y) { }
}
```
We mostly like both. The first syntax feels very simple and it effectively moves the "entire"
constructor signature up to the type declaration, instead of just the parameter list. However,
we don't think that class members would be in scope in the argument list for this base call
and there are some rare cases where arguments to base calls may involve calls to static private
helper methods in the class. Because of that we think the second syntax is more versatile and
reflects the full spectrum of options available in classes today.
#### Conclusion
Both syntaxes are accepted. If prioritization is needed, the base specification on the primary
constructor body is preferred.

View file

@ -1,95 +0,0 @@
# C# Language Design for May 4, 2020
## Agenda
Design review feedback
## Discussion
We had a design review on 2020-04-29 to bring our latest designs to the full review team and get
feedback. Today we went over the feedback and how it would affect our design.
### Final initializers
- Design review said it was very complicated, when do I use an initializer vs a constructor?
A possible fix would be to try to run initializers *before* constructors, instead of after. The
main problem is that this is not where object initializers (using setters) run today. It would be
very distasteful to have `init-only` setters run at a different time from regular setters, and
worse to subtly run the setters at a different time just because of the presence of a different
`init-only` field.
This is a difficult piece of feedback to reconcile, because it doesn't present a clear direction.
However, we're not sure we need to finish the design for final initializers now. We still think
the scenarios are useful, but there are many scenarios which don't rely on those semantics. One
of the most important scenarios that we were worried about was how to copy a type that had
private fields that should not be copied. One proposal was to write a final initializer which
either resets certain fields, or `throw`s if the state is invalid. Our proposed alternative for
this situation is to write your own copy constructor, which sets up the appropriate state for the
copy.
However, final initializers do address a significant shortfall in existing scenarios, namely that
there's no way to validate a whole object in a property setter (or initter). In that sense we do
have many existing issues, separate from our records designs, which would be addressable with the
feature. There is also no way to validate an object after a `with` expression since necessarily.
### Factory methods
The review team agreed about the necessity of "factory" semantics in the `with` expression, namely
that the with expression essentially requires a `virtual` Clone method to work correctly through
inheritance, but was not convinced that the feature was generally useful.
We're also not convinced that it's generally useful, but limiting `with` to only be usable on a
record is a significant change from where we were before, where records are currently fully
representable as regular classes.
We need to consider if we are willing to live with this limitation, or need a way of specifying
the appropriate `Clone` method in source.
### Structs as records
Can every struct be a record automatically? We don't need a `Clone` method, because structs
already copy themselves and they already implement value equality (albeit sometimes
inefficiently). If we take this stance, would we want to explicitly design records as "struct
behavior for classes?" If that's true, we would seek to use the behavior of structs as a template
for records.
### Positional records
The feedback was negative about making a primary constructor parameters different from positional
record parameters. The proposal during the design meeting was that primary constructors would see
parameters as "captured" in the scope of the class, while records would generate public
properties for each parameter. This is a big semantic divergence, as expressions like
`this.parameter` would be legal in the body of a positional record, but illegal in the body of a
class with a primary constructor. One way of shrinking the semantic gap would be to always
generate members based on primary constructor parameters, but in regular classes those members
would be private fields, while in records they would be public init-only properties. Even this
semantic difference was perceived as too inconsistent.
We have two proposals to unify the behavior inside and outside of records. On one end, we could
try to view primary constructors as a syntactic space to contain more elements. By default,
primary constructors would be simple parameters, which could be closed over in the class body. By
allowing member syntax in the parameter list, the user would have more control over the
declaration. For instance,
```C#
public class Person(
public string Name { get; init; }
);
```
would generate a public property named `Name` instead of simply a parameter and the property
would be implicitly assigned in the constructor.
On the other hand, we could _always_ make public properties, abandoning the idea of
primary-constructor-parameters-as-closures. In this formulation,
```C#
class C(int X, int Y);
```
would generate two properties, X and Y. If this is made into a record e.g., `data class C(int X,
int Y)`, then the same record members would be synthesized as in a nominal record.
We did not settle on a conclusion, but have a rough sense that having a primary constructor
always generate properties is preferred.

View file

@ -1,79 +0,0 @@
# C# LDM for May 6, 2020
## Agenda
1. `if (e is not int i)`
2. Target-typed conditional
3. Extension GetEnumerator
4. `args` in top-level programs
## Discussion
### `if (e is not int i)`
https://github.com/dotnet/csharplang/issues/3369
There are broader features that we'd to consider here as well, for instance allowing
some declarations below `or` patterns. However, this should be compatible with broader
changes and is easy to implement right now.
#### Conclusion
Accepted for C# 9. Further elaborations will be considered, assuming the schedule could
accept it.
### Target-typed conditional
We still unfortunately have a breaking change here with
```C#
M(b ? 1 : 2, 1); // calls M(long, long) without this feature; ambiguous without this feature
M(short, short);
M(long, long);
```
As always, breaking changes are very worrying, unless we are confident that almost no real-world
code would be broken. If the breaking change results in an ambiguity instead of silent different
codegen, that is substantially better, as people would at least know that the compiler changed
behavior. At the moment, we only think that this change could result in new ambiguities, not
different behavior.
#### Conclusion
We'll do some more investigation, try to find code that would be broken, and see if we can accept
the change.
### Extension GetEnumerator
https://github.com/dotnet/roslyn/issues/43147
Conclusions:
No objections to the proposals as written.
### `args` in Top-Level programs
If the top-level statements are logically inside a `Main` method, it would be very useful to have
access to the command line arguments for the program. You can access these via
`Environment.GetCommandLineArgs()`, but it's unfortunate that this is both different from the
APIs in Main, and `Environment.GetCommandLineArgs()` includes the program name, and `args` in
Main does not.
If we want to do something, we could have a magic variable named `args` (similar to `value` in
setters) or a property in the framework called `Args` (e.g. `Environment.Args`).
In favor of the property, fewer language-level changes means fewer things that people have to
learn.
In favor of the `args` magic variable, it's simpler to use than a property (since the property
would either have to qualified with a type name, or a `using static` would have to be added) and
a language feature for the inputs (command line args) mirrors the language feature for the output
(returning an `int` that turns into the process exit code).
#### Conclusion
We'll go with the `args` magic variable. We still need to decide on the scope: either equivalent
to top-level locals, which are visible in all files but inaccessible, or only in scope in
top-level statements. If we make it visible in all files we would only add the variable if there
is at least one top-level statement in the program.

View file

@ -1,107 +0,0 @@
# C# Language Design Meeting for May 11, 2020
## Agenda
Records
## Discussion
Today we tried to resolve some of the biggest questions about records,
namely how to unify the semantics of nominal records and positional
records, and what are the key scenarios that we are trying to resolve
with records.
The main inconsistency is that the members `record class Person(string FirstName, string
LastName)` are very different from the members in `class Person(string firstName, string
lastName)`. One way of resolving this is to unify the meaning of the declaration in the direction
of primary constructors. In this variant, the parameters of a primary constructor always capture
items by default.
To produce public init-only properties like we were exploring, we would require an extra
keyword, `data`, that could be generalizable. So a record which has two public init-only
members would be written
```C#
record Person(data string FirstName, data string LastName);
```
This would allow a generalizable `data` keyword that could be applied even in regular
classes, e.g.
```C#
class Person
{
data string FirstName;
data string LastName;
public Person(string first, string last)
{
}
}
```
The worry here is that we're harming an essential motivation for records, namely a short syntax
for immutable data types. In the above syntax, `record` alone does not mean immutable data type,
but instead only value equality and non-destructive mutation. A problem with this is that value
equality is dangerous for mutable classes, since the hash code can change after being added to a
dictionary. This was why we were previously cautious about adding a general feature for value
equality. One option to discourage misuse would be to provide a warning for any non-immutable
member in a record class.
The other problem is, frankly, it's not that short. Aside from some duplication of intent by
requiring both `record` and `data` modifiers, it also requires applying the `data` modifier to
each member, so the overhead grows larger as the type does.
Alternatively, we could go in the complete opposite direction: limit customizability by making
records all about public immutable data.
For instance, nominal records could also have syntax abbreviation
```C#
record Person { string FirstName; string LastName; }
```
and we could avoid confusion by prohibiting other members entirely.
This would look at lot more like positional records, e.g.
```C#
record Person(string FirstName, string LastName);
```
and we could introduce further restrictions on those by also disallowing other members
in the body, or even disallowing primary constructors entirely.
Disallowing all members inside of records is draconian, but not entirely without precedence.
Enums work the same way in C# and members are added via extensions methods. That's not a ringing
endorsement since we've considered proposals for allowing members in enums before, but it also
doesn't put it outside the realm of possibility for C#, especially in earlier forms.
The main drawback of the simplest form is the risk that we might have trouble evolving the
feature to fit all circumstances. If we wanted to allow a user to define private fields, the
syntax with no accessibility modifier now means "public init-only property" so we might not
be able to add support for private fields at all, or we might have to use a syntactic distinction
that requires a `private` accessibility, which is a subtle change.
### Conclusion
We largely prefer the short syntax for records. A nominal record would look like
```C#
record class Person { string FirstName; string LastName; }
```
This would create a class with public `init-only` properties named `FirstName`
and `LastName`, along with equality and non-destructive mutation methods.
Similarly,
```C#
record class Person(string FirstName, string LastName);
```
would create a class with all of the above, but also a constructor and Deconstruct.
We have yet to confirm whether `record` disallows private fields entirely, or if it
just changes the default accessibility.

View file

@ -1,147 +0,0 @@
# C# Language Design Meeting for May 27, 2020
## Agenda
1. Records -- syntax
## Discussion
### Syntax Questions
We got significant feedback that `record` is a better name than `data` for indicating
a `record`. There are two syntaxes we've been considering here:
1. `record class Person { ... }`
2. `record Person { ... }`
The main difference here is that (2) has less obvious space for a `struct` token, which
raises the question of whether "record structs" are a feature we want to enable.
There are a couple arguments for why records would be useful for structs. The first is
that "value behavior" is a general feature that could be useful for both structs and classes.
Value equality exists for structs today, but it is potentially slow in the runtime implementation.
The second is that the syntax provided for classes is also useful for structs. The positional
syntax specifically seems attractive because it has a lot of similarity to tuples and allows a
form of "named tuple."
```C#
record struct Point(int X, int Y);
```
On the other hand, we could improve the performance of equality, completely separate from records.
For instance, the compiler could add equality methods if they are not present. We also do not necessarily
need to address structs first. Since structs already have many features of records they are, in a sense,
"less far behind" than classes in record features. It makes sense to concentrate first on classes and
consider augmentations for structs in a future update.
So to return to the original question, we have to decide if we want to move forward with option (2), which
is a new form of declaration. Notably, this is a breaking change for certain scenarios e.g.,
```C#
record M(int X, int Y) { ... }
class C
{
record M(int X, int Y) { ... }
partial record M(int X, int Y);
}
```
All of these are currently method declaration syntax. In C# 9 this would be a record declaration
with a positional constructor and a body. Normally we would never consider this kind of change,
but since we started shipping with .NET Core we do not automatically upgrade language version
unless the target framework is the newest one (i.e., NET 5).
#### Conclusion
Do not support structs for now. They already support many features of records and we can add
more, time permitting.
The accepted proposal is that the syntax, `<modifiers> <attributes> 'record'` followed by
`identifier` and either '(', '{', or '<' would be contextually parsed as a record declaration
only if the language version is C# 9.
### Short-property syntax
We previously agreed that, to unify the syntax forms in the positional and nominal declaration, we
would allow fields in nominal records with no modifiers to instead be interpreted as public auto-properties.
After looking at feedback and exploring some of the related issues, we've decided that's not the best approach.
There are a few proposals on the table:
1. Leave positional records the same, do not provide special syntax for nominal records.
```C#
public record Point(int X, int Y);
public record Point
{
public int X { get; init; }
public int Y { get; init; }
}
```
2. Unify the declaration forms in favor of nominal records, allowing property declarations in the
record parameter list
```C#
public record Point(
public int X { get; init; },
public int Y { get; init; }
)
public record Point
{
public int X { get; init; },
public int Y { get; init; }
}
```
3. Keep positional records the same, provide a new modifier (e.g., `data` or `init`) for members
which means "public init-only property"
```C#
public record Point(int X, int Y);
public record Point
{
data int X;
data int Y;
}
```
4. Provide the new modifier from (3), and require it in both types of records
```C#
public record Point(data int X, data int Y);
public record Point
{
data int X;
data int Y;
}
```
After discussion, we prefer (3). Positional records already seem to have enough syntactic distinction and
the `data` keywords seem superfluous in this position. It also makes the shortest syntax form match up
with the most common use case.
However, we do think some further keyword is necessary for nominal records. Looking too much like existing
fields seems like it would be too confusing, especially if we want to also allow fields in records.
Instead, we're considering leaving positional records to generate public auto-properties by default, partly
because they are already a significantly different syntax that cannot be confused with existing language
constructs, and providing a new mechanism for positional records.
#### Conclusion
Keep positional records the same, provide a `data` modifier for fields which means "public
init-only property"
```C#
public record Point(int X, int Y);
public record Point
{
data int X;
data int Y;
}
```

View file

@ -1,163 +0,0 @@
# C# Language Design for June 1, 2020
## Agenda
Records:
1. Base call syntax
2. Synthesizing positional record members and assignments
3. Record equality through inheritance
## Discussion
### Record base call syntax
We'd like to reconsider adding a base call syntax to a record declaration, i.e.
```antlr
record_base
: ':' class_type argument_list?
| ':' interface_type_list
| ':' class_type argument_list? interface_type_list
;
```
The main question is how the scoping of the parameters from the record positional
constructor interact with the base call syntax and the record body.
We would definitely like the parameters to be in scope inside the base call. For the record body,
it's proposed that the parameters of the primary constructor are in scope for initializers, and
the primary constructor body (if we later accept a proposal for such syntax). The parameters
shadow any members of the same name. The parameters are not in scope outside of these locations.
To unify the scoping behavior between the base call and the body, we propose that members of the
body are also in scope in the base call syntax. Instance members would be an error in these locations
(similar to how instance members are in scope in initializers today, but an error to use), but
the parameters of the positional record constructor would be in scope and useable. Static members
would also be useable, similar to how base calls work in ordinary constructors today.
**Conclusion**
The above proposals are accepted.
### Synthesized positional record members
A follow-up question is how to do generation for auto-generated positional properties. We need
to decide both 1) when we want to synthesize positional members and 2) when we want to initialize
the corresponding members. The affect is most clearly visible in the example below, where the
initialization order will affect what values are visible at various times during construction,
namely whether the synthesized properties are initialized before or after the `base` call.
```C#
record Person(string FirstName, string LastName)
{
public string Fullname => $"{FirstName} {LastName}";
public override string ToString() => $"{FirstName} {LastName}";
}
record Student(string FirstName, string LastName, int Id)
: Person(FirstName, LastName)
{
public override string ToString() => $"{FirstName} {LastName} ({ID})";
}
```
First we discussed when to synthesize members, namely when an "existing" member will prevent
synthesis. A simple rule is that we synthesize members when there is no accessible, concrete
(non-abstract) matching member in the type already, either because it was inherited or because it
was declared. The rule for matching is that if the member would be considered identical in signature,
or if it would require the `new` keyword in an inheritance scenario, those members would "match." This
rule allows us to avoid generating duplicate members for record-record inheritance and also produces the
intuition that we should err on the side of not synthesizing members when they could be confused with
an existing member.
Second, we discussed when and in what order assignments were synthesized from positional record
parameters to "matching" members. A starting principle is that in record-record inheritance we don't
want to duplicate assignment -- the base record will already assign its members. In that case, we could
choose to assign only members synthesized or declared in the current record. That would mean
```C#
record R(int X)
{
public int X { get; init; }
}
```
would initialize the `X` property to the value of the constructor parameter even though the property
is not compiler synthesized. However, we would have to decide if it is synthesized before or after
the `base` call. In essence, the question is how we de-sugar the assignments. Is `record Point(int X, int Y);`
equivalent to
```C#
record Point(int X, int Y) : Base
{
public int X { get; init; } = X;
public int Y { get; init; } = Y;
}
```
or
```C#
record Point(int X, int Y) : Base
{
public int X { get; init; }
public int Y { get; init; }
public Point
{
this.X = X;
this.Y = Y;
}
}
```
Note that today property and field initializers are always executed before the `base` call, while
statements in the constructor body are executed afterwards and we are disinclined to change that
for record initializers.
Looking at the examples as a whole, we think using the initializer behavior is good -- it's easy
to understand and more likely to be correct in the presence of a virtual call in the base class,
but it makes things significantly more complicated if we synthesize it even for user-written
properties. Is the initializer synthesized even if there's already an initializer on the property?
What if the user-written property isn't an auto-property?
**Conclusion**
We think it's much clearer if we simplify the rules to only initialize synthesized properties.
Effectively, if you replace the synthesized record property, you also have to write the initialization,
if you want it. In the case that the property is not already declared, e.g. `record Point(int X, int Y);`
the equivalent code is
```C#
record Point(int X, int Y)
{
public int X { get; init; } = X;
public int Y { get; init; } = Y;
}
```
### Equality through inheritance
We have a number of small and large questions about how records work with inheritance.
Q: What should we do if one of the members which we intend to override, like object.Equals and
object.GetHashCode, are sealed?
A: Error. This is effectively outside of the scope of automatic generation.
Q: Should we generate a strongly-typed Equals (i.e., `bool Equals(T)`) for each record declaration? What
about implementing `IEquatable<T>`?
A: Yes. Implementing `IEquatable<T>` is very useful and would require a strongly-typed equals method. We
could explicitly implement the method, but we also think this is useful surface area. If we broaden
support to structs, this would prevent a boxing conversion, which has a significant performance impact.
Even for classes this could avoid extra type checks and dispatch.
Q: Should each record declaration re-implement equality from scratch? Or should we attempt to dispatch
to base implementations of equality?
A: For the first record in a type hierarchy, we should define equality based on all the accessible fields,
including inherited ones, in the record. For records inheriting from a class with an existing
`EqualityContract`, we should assume that it implements our contract appropriately, and delegate comparing
the EqualityContract itself and the base fields to the base class.

View file

@ -1,41 +0,0 @@
# C# LDM for June 10, 2020
## Agenda
1. "Roles"
## Discussion
Exploration of previous proposal: #1711
This is a topic that we've explored before which we're reviving for further consideration and discussion.
We have a "role" proposal, but it's more of a starting point for a final design. There are a number of
different problems we can presumably solve here, but it seems like we have some intersecting features that
might address multiple problems simultaneously.
There are many tradeoffs to consider in these designs. One of the most well-known is sometimes called
"incoherence," where the ability to implement an interface in two ways on the same type effectively causes
the two implementations to "cross" each other in ways that can be hard to predict. For instance, if two
people implemented `IEquatable<T>` on the same third-party type, and both added it to a dictionary, if they
used different `GetHashCode` implementations then the same member could be added twice, and each consumer
wouldn't see the implementation used by other consumers.
Another tradeoff is the ability to use the role as a type, namely refer to it in a type position. This
is often desirable, but has some tradeoffs in type equivalence (see SML modules for alternative notions
of type equivalence through functors).
The Roles proposal as a whole seems very powerful, but there are many big questions here. The biggest,
most pressing question is: what problems do we think are the most important and how big a feature do
we need to address them? Providing a way to abstract over different numeric abstractions is a concrete
scenario, but it may not need the fully generalized mechanism. Allowing existing types to conform
to an abstract after definition is also powerful and has many possible use cases, but how flexible
do we need to make that mechanism? Can it only be used in generics? Can you implement abstractions
defined in other compilations on types defined in other compilations?
The performance concerns are also very real. We have a few mechanisms for abstraction in the language
today, but a lot of those mechanisms come with performance costs like allocation that make them
unusable in performance-sensitive scenarios. We would like more zero-cost abstractions if possible,
but we're not sure what functionality we could provide in those circumstances and whether the features
would fit well into the existing ecosystem.

View file

@ -1,197 +0,0 @@
# C# Language Design Meeting for June 15, 2020
## Agenda
1. `modreq` for init accessors
1. Initializing `readonly` fields in same type
1. `init` methods
1. Equality dispatch
1. Confirming some previous design decisions
1. `IEnumerable.Current`
## Discussion
### `modreq` for init accessors
We've confirmed that the modreq design for `init` accessors:
- The modreq type `IsExternalInit` will be present in .NET 5.0, and will be recognized if
defined in source
- The feature will only be fully supported in .NET 5.0
- Usage of the property (including the getter) will not be possible on older compilers, but
if the compiler is upgraded (even if an older framework is being used), the getter will be
usable
### Initializing `readonly` fields in same type
We previously removed `init` fields from the design proposal because we didn't think it was
necessary for the records feature and because we didn't want to allow fields which were
declared `readonly` before records to suddenly be settable externally in C# 9, contrary to
the author's intent.
One extension would be to allow `readonly` fields to be set in an object initializer only inside
the type. In this case you could still use object initializers to set readonly fields in
static factories, but because they would be a part of your type you would always know the intent
of the `readonly` modifier. For instance,
```C#
class C
{
public readonly string? ReadonlyField;
public static C Create()
=> new C() { ReadonlyField = null; };
}
```
On the other hand, we may not need a new feature for many of these scenarios. An init-only
property with a private `init` accessor behaves similarly.
```C#
class C
{
public string? ReadonlyProp { get; private init; }
public static C Create()
=> new C() { ReadonlyProp = null; };
}
```
**Conclusion**
We still think `readonly` fields are interesting, but we're not sure of the scenarios yet.
Let's keep this on the table, but leave it for a later design meeting.
### `init` methods
We previously considered having `init` methods which could modify `readonly` members just
like `init` accessors. This could enable scenarios like "immutable collection initializers",
where members can be added via an `init` Add method.
The problem is that the vast majority of immutable collections in the framework today would be
unable to adopt this pattern. Collection initializers are hardcoded to use the name `Add` today
and almost all the immutable collections already have `Add` methods that are meant for a different
purpose -- they return a copy of the collection with the added item.
If we naively extend `init` to collection initializers most immutable collections wouldn't be able
to adopt them because they couldn't make their `Add` methods `init`-only, and no other method name
is allowed in a collection initializer. In addition, some types, like ImmutableArrays, would be
forced to implement init-only collection initializers very inefficiently, by declaring a new array
and copying each item every time `Add` is called.
**Conclusion**
We're still very interested in the feature, but we need to determine how we can add it in a way
that provides a path forward for our existing collections.
### Equality dispatch
We have an equality implementation that we think is functional, but we're not sure it's the most
efficient implementation. Consider the following chain of types:
```C#
class R1
{
public override bool Equals(object other)
=> Equals(other as R1);
public virtual bool Equals(R1 other)
=> !(other is null) &&
this.EqualityContract == other.EqualityContract
/* && compare fields */;
}
class R2 : R1
{
public override bool Equals(object other)
=> Equals(other as R2);
public override bool Equals(R1 other)
=> Equals(other as R2);
public virtual bool Equals(R2 other)
=> base.Equals((R1)other)
/* && compare fields */;
}
class R3 : R2
{
public override bool Equals(object other)
=> Equals(other as R3);
public override bool Equals(R1 other)
=> Equals(other as R3);
public override bool Equals(R2 other)
=> Equals(other as R3);
public virtual bool Equals(R2 other)
=> base.Equals((R1)other)
/* && compare fields */;
}
```
The benefit of the above strategy is that each virtual call goes directly
to the appropriate implementation method for the runtime type. The drawback
is that we're effectively generating a quadratic number of methods and overrides
based on the number of derived records.
One alternative is that we could not override the Equals methods of anything
except our `base`. This would cause more virtual calls to reach the implementation,
but reduce the number of overrides.
**Conclusion**
We need to do a deep dive on this issue and explore all the scenarios. We'll come
back once we've outlined all the options and come up with a recommendation.
### Affirming some previous decisions
Proposals for copy constructors
- Do not include initializers (including for user-written copy constructors)
- Require delegation to a base copy constructor or `object` constructor
- If the implementation is synthesized, this behavior is synthesized
Proposal for Deconstruct:
- Doesn't delegate to a base Deconstruct method
- Synthesized body is equivalent to a sequence of assignments of member
accesses. If any of these assignments would be an error, an error is produced.
It's also proposed that any members which are either dispatched to in a derived record
or expected to be overridden in a derived record will produce an error for synthesized
implementations if the required base member is not found. This includes if the base
member was not present in the immediate base, but was inherited instead. For some situations
this may mean that the user can write a substituted implementation for that synthesized
member, but for the copy constructor this effectively forbids record inheritance, since
the valid base member must be present even in a user-defined implementation.
**Conclusion**
All of the above decisions are upheld.
### Non-generic IEnumerable
Currently in the framework `IEnumerable.Current` (the non-generic interface) is annotated to
return `object?`. This produces a lot of warnings in legacy code that `foreach` over the result
with types like `string`, which is non-nullable. We have two proposals to resolve this:
- Un-annotate `IEnumerable.Current`. This will keep the member nullable-oblivious and no warnings
will be generated, even if the property is called directly
- Special-case the compiler behavior for `foreach` on `IEnumerable` to suppress nullable warnings
when calling `IEnumerable.Current`
**Conclusion**
Overall, we prefer un-annotation. Since this interface is essentially legacy, we feel that
providing nullable analysis is potentially harmful and rarely beneficial.

View file

@ -1,122 +0,0 @@
# C# Language Design Meeting for June 17, 2020
## Agenda
1. Null-suppression & null-conditional operator
1. `parameter!` syntax
1. `T??`
## Discussion
### Null-suppression & null-conditional operator
Issue #3393
We generally agree that this is uninintended and unfortunate, essentially a spec bug. The
only question is whether to allow `!` at the end of a `?.`, as well as in the "middle". In
some sense
### `parameter!` syntax
This has been delayed because we haven't been able to agree on the syntax. The main contenders
are
```C#
void M(string param!)
void M(string param!!)
void M(string! param)
void M(string !param)
void M(checked string param)
void M(string param ?? throw)
void M(string param is not null)
void M(notnull string param)
void M(null checked string param)
void M(bikeshed string param)
void M([NullChecked("Helper")] string param)
/* contract precondition forms */
void M(string param) Requires.NotNull(param)
void M(string param) when param is not null
```
The simplest form, `void M(string param!)` is attractive, but looks very similar to the null
suppression operator. The biggest problem is that you can see them as having very different
meanings -- `!` in an expression silences nullable warnings, while `!` on a parameter does the
opposite, it actually produces an exception on nulls. However, the result of both forms is a
value which is treated by the compiler as not-null, so there is a way of seeing them as similar.
Moving it to the other side of the parameter name, `!param`, would resolve some of the similarity
with the null suppression operator, but it also looks a lot like the `not` prefix operator. There's
slightly less contradiction in these operators, but it still features a bit of syntactic overloading.
`string!` has a couple problems, including a suggestion that it's a part of the type (which it would not
be), and that sometimes you may want to use the operator on nullable types, like `AssertNotNull` methods.
It also wouldn't be usable in simple lambdas without types.
`checked` suggests integer over/underflow more than nullability.
`param!!` has some usefulness that we could provide a corresponding expression form -- `!`
suppresses null warnings, while `!!` actually checks for null and throws if it is found. On the
other hand, it also reads a bit strangely, especially since we're adding a new syntax form
instead of trying to reuse some forms we already have. On the other hand, the fact that it's
different enough to look different, while also short enough to be used commonly has a lot in
favor of it. In general we historically have a bias towards making new things strongly
distinguished from existing code, but shortly after introducing the feature we tend to wish that
things were less verbose and didn't draw as much attention in the code. On the other hand, the
nullable feature has a rule that it should not affect code semantics, while the purpose of this
feature is to affect code semantics. `param!!` could be seen as being too similar to other things
in the nullable feature, but to some people it also stands out because of the multiple operators.
We did a brief ranked choice vote and came up with the following ranking, not as definitive, just to
measure our current preferences:
1. `void M(string param!!)`
2. `void M(nullcheck string param)`
3. `void M([NullChecked(Helper)] string param)`
Many people don't have strong opinions, so we don't have a clear winner coming out.
**Conclusion**
We're getting closer to consensus, but we need to discuss this more and consider some of the
long-term consequences, as well as impact on other pieces of the language design.
### `T??`
Unfortunately there are multiple parsing ambiguities with `T??`:
```C#
(X??, Y?? y) t;
using (T?? t = u) { }
F((T?? t) => t);
```
This has left us looking back to the original syntax: `T?`. The original reason we rejected this
was that there could be confusion that `T?` means "maybe default," so that the type is nullable
if it's a reference type, but not nullable if it's a value type.
If we want to allow the `T?` syntax anyway, we need some syntax to specify that, for overrides,
we want the method with no constraints (unconstrained). This is because constraints cannot
generally be specified in overrides or explicit implementations, so previously `T?` always meant
`Nullable<T>`, but now it may not. We added the `class` constraint for nullable reference types,
but neither `T : class` nor `T : struct` help if the `T` is unconstrained. In essence, we need a
constraint that means unconstrained. Some options include:
```C#
override void M1<[Unconstrained]T,U>(T? x) // a
override void M1<T,U>(T? x) where T: object? // b
override void M1<T,U>(T? x) where T: unconstrained // c
override void M1<T,U>(T? x) where T: // d
override void M1<T,U>(T? x) where T: ? // e
override void M1<T,U>(T? x) where T: null // f
override void M1<T,U>(T? x) where T: class|struct // g
override void M1<T,U>(T? x) where T: class or struct // h
override void M1<T,U>(T? x) where T: cluct // joke
override void M1<T,U>(T? x) where T: default // i
```
**Conclusion**
The `default` constraint seems most reasonable. It would only be allowed in overrides and
explicit interface implementations, purely for the purpose of differentiating which method
is being overridden or implemented. Let's see if there are other problems.

View file

@ -1,104 +0,0 @@
# C# Language Design Meeting for June 22, 2020
## Agenda
1. Data properties
1. Clarifying what's supported in records for C# 9
- Structs
- Inheritance with records and classes
## Discussion
### `data` properties
We've been working on an implementation for data properties and have some questions about
modifiers, which are currently not allowed.
There are some modifiers that could be legal, like `new`, `abstract`, `virtual`, and `override`.
Some of these, especially `new` and `override` would probably prevent `data` properties from
being used at all, since a warning would be generated if there's a matching member in the base
that requires either `override` or `new`.
However, the point of data properties was to be brief. Adding modifiers would potentially cloud
the purpose of the feature. The syntactical abbreviation isn't strictly required because the
equivalent property can always be written explicitly:
```C#
data int X;
// versus
public int X { get; init; }
```
**Conclusion**
Based on where we are in the schedule for C# 9, we probably will not have time to respond to feedback.
Therefore, we're planning on putting this feature into preview and not shipping it with C# 9. For now,
let's allow the following modifiers: `public`, `new`, `abstract`, `virtual`, and `override`. This will
provide an option for allowing `data` properties to interact more smoothly with other constructs in C#,
while maintaining some of the simplicity. We'll see how it feels in prototypes to gauge whether we went
too far, or not far enough.
### Struct records
We're not sure how much space we have for structs in C# 9, but we do think structs are an
interesting design point. We previously proposed that structs could support a variety of record
behaviors without being a record. One idea would be to automatically implement `IEquatable<T>`
for all structs, but this has a lot of potential negative impact for the runtime and the
ecosystem: effectively every struct would add a large number of members which could seriously
bloat metadata.
An alternative would be to work with the runtime to try to provide a runtime-generated form of
`IEquatable<T>`. If it's cheap enough, we may be able to provide `IEquatable<T>` for structs as
a as-necessary addition to all structs. However, this is a potentially very large change. Even
small costs could add up, and even small semantic changes could impact someone.
Overall, the modification of arbitrary struct semantics just seems too risky. Something as simple
as testing a struct for an implementation of `IEquatable<T>` could be a breaking change, and with
a change this big, it's almost certain to be breaking for someone.
**Conclusion**
Altering the semantics of all structs as-is is out. We're leaning towards a real "record struct"
declaration.
### Inheritance between records and classes
We currently have a restriction that records cannot inherit from classes and classes cannot
inherit from records. We should confirm that this restriction is acceptable for C# 9.
**Conclusion**
We definitely see scenarios for expanding this, but we think the restriction is fine for the
first version of records. Based on feedback and example scenarios, we can reconsider these
restrictions and also the expected semantics.
### `with` expressions on non-records
This encompasses:
1. Finding some way to define a compatible `Clone` for user-defined classes
1. Anonymous types
1. Tuples
Compatibility for `Clone` in user-defined classes is the most difficult of these items because it
relies on a new language feature for requiring that a new object is returned from the Clone
method.
Anonymous types are very simple, we can retroactively define them as record types.
Tuples would be simple to special-case in the language, but it's probably more consistent to
decide the semantics for struct records first, and then update the definition of
System.ValueTuple to support them.
**Conclusion**
Anonymous types can be updated at any time, everything else should wait until after C# 9.

View file

@ -1,188 +0,0 @@
# C# Language Design Meeting for June 24th, 2020
## Agenda
1. [Confirming Function Pointer Decisions](#Confirming-Function-Pointer-Decisions)
1. [Parameter Null Checking](#Parameter-Null-Checking)
1. [Interface `static` Member Variance](#Interface-`static`-Member-Variance)
1. [Property Enhancements](#Property-Enhancements)
## Quote of the Day
"It feels a little bit like we're playing code golf here"
## Discussion
### Confirming Function Pointer Decisions
https://github.com/dotnet/roslyn/issues/39865#issuecomment-647692516
There are a few open questions from a previous [LDM](LDM-2020-04-01.md) and a followup email chain
that need to be confirmed before they can be implemented. These questions center around calling
convention type lookup and how identifiers need to be written in source. The grammar we had roughly
proposed after the previous meeting is:
```antlr
func_ptr_calling_convention
: 'managed'
| 'unmanaged' ('[' func_ptr_callkind ']')?
func_ptr_callkind
: 'CallConvCdecl'
| 'CallConvStdcall'
| 'CallConvThiscall'
| 'CallConvFastcall'
| identifier (',' identifier)*
```
##### Calling Convention Lookup
When attempting to bind the `identifier` used in an unmanaged calling convention, should this follow
standard lookup rules, such that the type must be in scope at the current location, or is using a
form of special lookup that disregards the types in scope at the current location? The types valid
in this location are a very specific set: they must come from the `System.Runtime.CompilerServices`
namespace, and the types must have been defined in the same assembly that defines `System.Object`,
regardless of the binding strategy used here, so it's really a question of whether the user has to
include this namespace in their current scope, adding a bunch of types that they are generally not
advised to use directly, and whether they can get an error because they defined their own calling
convention.
##### Conclusion
Given the specificness required here, we will use special name lookup.
##### Required identifiers
The previous LDM did not specify the required syntax for the identifiers quite explicitly enough for
implementation, and specified that identifiers should be lowercase while also having upper case
identifiers in some later examples. The following rules are proposed as the steps the compiler will
take to match the identifier to a type:
1. Prepend `CallConv` onto the identifier. No casemapping is performed.
2. Perform special name lookup with that typename in the `System.Runtime.CompilerServices` namespace
only considering types that are defined in the core library of the program (the library that defines
`System.Object` and has no dependencies itself).
We also reconsidered the decision from the previous LDM on using lowercase mapping for the identifier
names. There is convention for this in other languages: C/C++, for example, use `__cdecl` or similar
as their calling convention specifiers, and given that this feature will be used for native interop
with libraries doing this it would be nice to have some parity. However, this would introduce several
issues with name lookup: existing special name lookup allows us to modify the `identifier` specified
in source, but it does not allow us to modify the names of the types we're matching against, which
we would need to do here. There is certainly an algorithm that could be specified here, but we overall
felt that this was too complicated for what was a split aesthetic preference among members.
##### Conclusion
The proposed rules are accepted. As a consquence, the identifier specified in source cannot start
with `CallConv` in the name, unless the runtime were to add a type like `CallConvCallConv`.
### Parameter Null Checking
We ended the [previous meeting](LDM-2020-06-17.md) on this with two broad camps: support for the `!!`
syntax, and support for some kind of keyword. Email discussion over the remainder of the week and
polling showed that a clear majority supported the `!!` syntax.
#### Conclusion
We will be moving forward with `!!` as the syntax for parameter null checking:
```cs
public void M(Chitty chitty!!)
```
### Interface `static` Member Variance
https://github.com/dotnet/csharplang/issues/3275
We considered variance in `static` interface members. Today, for co/contravariant type parameters
used in these members, they must follow the full standard rules of variance, leading to some
inconsistency with the way that `static` fields are treated vs `static` properties or methods:
```cs
public interface I<out T>
{
static Task<T> F = Task.FromResult(default(T)); // No problem
static Task<T> P => Task.FromResult(default(T)); //CS1961
static Task<T> M() => Task.FromResult(default(T)); //CS1961
static event EventHandler<T> E; // CS1961
}
```
Because these members are `static` and non-virtual, there aren't any safety issues here: you can't
derive a looser/more restricted member in some fashion by subtyping the interface and overriding
the member. We also considered whether this could potentially interfere with some of the other
enhancements we hope to make regarding roles, type classes, and extensions. These should all be
fine: we won't be able to retcon the existing static members to be virtual-by-default for interfaces,
as that would end up being a breaking change on multiple levels, even without changing the variance
behavior here.
We also considered whether this change could be considered a bug fix on top of C# 8, meaning that
users would not have to opt into C# 9 in order to see this behavior. While the change is small and
likely very rarely needed, we would still prefer to avoid breaking downlevel compilers.
#### Conclusion
We will allow `static`, non-virtual members in interfaces to treat type parameters as invariant,
regardless of their declared variance, and will ship this change in C# 9.
### Property Enhancements
In a [previous LDM](#LDM-2020-04-01.md) we started to look at various enhancements we could make
to properties in response to customer feedback. Broadly, we feel that these can be addressed by
one or more of the following ideas:
1. Introduce a `field` contextual keyword that allows the user to refer to the backing storage of
the property in the getter/setter of that property.
* In this proposal, we can consider all properties today as having this `field` keyword. As
an optimization, if the user does not refer to the backing field in the property body, we
elide emitting of the field, which happens to be the behavior of all full properties today.
* Of the proposals, this allows the most brevity for simple scenarios, allowing some lazily-fetched
properties to be one-liners.
* This proposal does not move the cliff far: if you need to have a type differing from the
type of the property, or multiple fields in a single property, then you must fall back to a full
property and expose the backing field to the entire class.
* There are also questions about the nullability of these backing properties: must they be
initialized? Or should we provide a way to declare them as nullable, despite the property
itself not being nullable?
* There was also concern that this is adding conceptual overhead for not enough gain: education
would be needed on when backing fields are elided and when they are not, complicated by the
additional backcompat overhead of ensuring that `field` isn't treated as a keyword when it
can bind to an existing name.
2. Allow field declarations in properties.
* Conveniently for this proposal, a scoping set of braces for properties already exists.
* This addresses the issue of properties backed by a different storage type: if you have a
property that uses a `Lazy<T>` to initialize itself on first access, for example.
* This also allows users to declare multiple backing fields, if they want to lock access
to the property or combine multiple pieces of information into a single type in the public
surface area.
* In terms of user education, this is the simplest proposal. Since a set of braces already
exists, the education is just "There's a new scope you can put fields in."
3. Introduce a delegated property pattern into the language.
* There have been a few proposals for this, such as #2657. Other languages have also adopted
this type of feature, including Kotlin and Swift.
* This is by far the most complex of the proposals, adding new patterns to the language and
requiring declaration of a whole new type in order to remove one or possibly 2 fields from
a general class scope.
* By that same token, however, this is the most broad of the proposals, allowing users to
write reusable property implementations that could be abstracted out.
The LDM broadly viewed these proposals as increasing in scope: the `field` keyword allows the most
brief syntax, but forces users off the cliff back to full class-scoped fields immediately if their
use case is not having a single backing field of the same type. Meanwhile, property-scoped fields
don't allow for and encourage creating reusable helpers, like delegated properties would.
We also recognize that regardless of what decisions we make today, we're not done in this space.
None of these proposals are mutually exclusive, and we can "turn the crank" by introducing one, and
then adding more in a future release. There is interest among multiple LDM members in adding some
form of reusable delegated properties or property wrappers, and adding one of either the `field`
keyword or property-scoped fields does not preclude adding the other in a later release. Further,
all of these proposals are early enough that we still have a bunch of design space to work through
with them, while designing ahead enough to ensure that we don't add a wart on the language that we
will regret in the future.
#### Conclusion
A majority of the LDM members would like to start by exploring the property-scoped locals space.
We'll start by expanding that proposal with intent to include in C# 10, but will keep the other
proposals in mind as we do so.

View file

@ -1,80 +0,0 @@
# C# Language Design Meeting for June 29th, 2020
## Agenda
1. [Static interface members](https://github.com/Partydonk/partydonk/issues/1)
## Procedural Note
For the past few years, the LDM notes have been compiled by the wonderful Andy Gocke (@agocke).
Andy is taking the next step in his career and moving to be the lead of the CLR App Model team
as of today, and as such will be moving to LDT emeritus status, joining the ranks of our other
former LDT members who form our advisory council. We thank him for all his hard work during his
time as notetaker, collating the sometimes inane rambling of the LDM in a set of readable
arguments and decisions.
At this point Fred Silberberg (@333fred) will be the new LDM notetaker. While I'll try to keep
up much of the same spirit as Andy, I am not going to try to match his exact style or formatting,
so there could be some changes in the exact formatting of the notes. I'll additionally apologize
in advance as the notes inevitably end up delayed in the first few weeks as I settle in. And now,
on to the meeting notes!
## Quote of the Day
"So we spent these last 10 years making it so we can come back to this. We actually never forgot,
[.NET Core] has all been a ploy to get statics into interfaces."
## Discussion
Today @abock and @migueldeicaza presented their work on Partydonk over the past year, bringing
abstract interface members to C# and the Mono runtime in a proof of concept. I did not take notes
on the specific slides: all of that material is available publicly on the partydonk repo
[here](https://github.com/Partydonk/partydonk/blob/master/Generic%20Math/Generic%20Math%20in%20.NET%20-%20Contractual%20Static%20Interface%20Members%20in%20CSharp.pdf).
Overall, the LDT members had a very positive reception to this work: in particular, they arrived
at many of the same conclusions @CarolEidt had in her exploration of this space 10 years ago:
much of the runtime work falls out from allowing `abstract static` in CIL and emitting constrained
calls to such members during compilation. From a language perspective, there's still a bit of work
to do. `override` on `abstract` static members defined in interfaces isn't particularly nice from
a regularity with instance members perspective. We will also have to do design work around explicit
implementations: it could all fall out from existing rules, but will need a critical eye to make
sure that the consequences of explicitly implementing an abstract member, and what that really means.
The existing code uses a `TSelf` generic parameter in what could be considered kind of an unfortunate
syntax:
```cs
interface INumeric<TSelf> where TSelf : INumeric<TSelf>
{
abstract static TSelf Zero { get; }
abstract static TSelf operator +(TSelf a, TSelf b);
}
```
The `TSelf : where TSelf : INumeric<TSelf>` was suggested in the past to help the prototype make
progress without blocking on associated types, but it's ugly and proliferates through the entire
type hierarchy if left unchecked. A much better solution would be to formally introduce associated
types into the language, something that we've discussed in LDM before and has some support. We should
take those into account here: if we introduce this entire type hierarchy, then in the next C# release
introduce a `TSelf` associated type it would leave an annoying blemish on the language. In particular,
we need to make sure we have a good roadmap for all components involved here: the compiler, the runtime,
and the core libraries. Nonvirtual static members in interfaces have already shipped with C# 8, so we
can't get that back and declared them virtual members of the interface.
We do still have a few language questions: today, you cannot create user-defined operators that involve
interfaces, because doing so would subvert the type system. However, some numeric applications seem
like they would want to be able to do this for constants (see `IExpressibleByIntegerLiteral` and
`IExpressibleByFloatLiteral` in the presentation). If we allow this for the `TSelf` parameter, it seems
like these concerns should be obviated: you're not converting to an interface type, you're converting to
a concrete type and specifying that said conversion must be available for any implementations of the
interface. Additionally there's an interesting correspondance issue: today, any members that are part
of the interface contract are available on an instance of the interface. However, with static methods,
the interface itself doesn't provide them: types that implement the interface provide them, but not the
interface itself. We can certainly come up with new rules to cover this, but we will need to do so.
Some other miscellaneous topics that came up:
* We could use this system to specify constructors with multiple parameters of a specific type. Static
interface members could be implemented by a type calling `new`, which would allow us to improve on the
simple `new()` that we have today.
* Existing language built-in operators would need to be considered to satisfy the constraints of the
numeric interfaces.

View file

@ -1,129 +0,0 @@
# C# Language Design Meeting for July 1st, 2020
## Agenda
1. [Non-defaultable struct types and parameterless struct constructors](#Non-defaultable-struct-types-and-parameterless-struct-constructors)
2. [Confirming unspeakable `Clone` method implications](#Confirming-unspeakable-Clone-method-implications)
## Quote of the Day
"I did not say implications, I said machinations [pronounced as in British English]. I used a big word."
"You mispronounced machinations [pronounced as in American English], which is why I'm just ignoring you."
## Discussion
### Non-defaultable struct types and parameterless struct constructors
Proposal: https://github.com/dotnet/csharplang/issues/99#issuecomment-601792573
#### Parameterless struct constructors
We discussed both proposals for allowing default struct constructors and for having a feature to allow differentiating between
`struct` types that have a valid `default` value, and types that do not.
First, we looked at parameterless constructors. Today, `struct`s cannot define their own custom parameterless constructors, and
a previous attempt to ship this feature in C# 6 failed due to a framework bug that we could not fix.
```cs
public struct S
{
public readonly int Field = 1; // Field is set by the default constructor
}
public void M<T>()
{
var s = (S)Activator.CreateInstance(typeof(T));
Console.WriteLine(s.Field);
}
```
In .NET Framework and .NET Core prior to 2.0, a call to `M` will print `0`. However, .NET Core fixed this API in 2.0 to correctly
call the parameterless constructor of a struct if one is present, and since we now tie language version to the target platform
there will be no supported scenario with this bug.
While there was general support for this scenario in the LDM, we spent most of the time on the second part of the proposal and
did not come away with a conclusion for parameterless constructors. We will need to revisit this in context of a reworked defaultable
types proposal and make a yes/no conclusion on this feature.
#### Non-defaultable struct types
The crux of this proposal is that we would extend the tracking we introduced in C# 8 with nullable reference types, and extend it
to value types that opt-in, holes and all. From a type theory perspective, the idea is that by applying a specific attribute, a
struct type can indicate that `default` and `new()` are _not_ the same value in the domain of its type. In fact, if the struct does
not provide a parameterless constructor, `new()` wouldn't be in the domain of the struct at all. This attribute would further opt the
struct's `default` value into participating in "invalid" scenarios in the same way that `null` is part of the domain of a reference
type, but is considered invalid for accessing members directly on that instance. This played well with our previous design of `T??`,
if we were to allow the `??` moniker on types constrained to `struct` as well as unconstrained type parameters. However, as `??`
has been removed from C# 9 due to syntactic ambiguities (notes [here](LDM-2020-06-17.md#T??)), that part of the proposal will have
to be reworked. Not having `??` makes the feature much harder to explain to users, and we'll run into issues with representation in
non-generic scenarios.
One thing that is clear from discussion is that non-defaultable struct types will need to have some standardized form of checking
whether they are the default instance. `ImmutableArray<T>`, for example, has an `IsDefault` property, as do most of the existing
struct types in Roslyn that cannot be used when `default`. We would want to be able to recognize this pattern in nullable analysis,
just like we do today with the `is null` pattern. Since the attribute and pattern would be new, we could declare it to be whatever
we desire, and the libraries will standardize around that if they want to participate.
Generics also present an interesting challenge for non-defaultable value types. Today, the `struct` constraint implies new:
```cs
public void M<T>() where T : struct
{
var y = new T(); // Perfectly valid
}
```
If we were to enable non-defaultable struct types, this would change: `new()` is not necessarily valid on all struct types because
non-defaultable struct types have explicitly opted to separate `default` and `new()` in their domain, and might not have provided
a value for `new()`, meaning that it would return `default`. From an emit perspective, this is further complicated: for the above
code, the C# compiler _already_ emits a `new()` constraint. C# code cannot actually specify both the `struct` constraint and the
`new()` constraint at the same time today, but in order to actually emit the combination of these constraints for this feature
we would have to introduce a new annotation on the type parameter to describe that it is required to have a parameterless `new()`
that provides a valid instance of the type.
`ref` fields in structs also came up in discussion. This is a feature that we've been asked for by the runtime team and a few other
performance-focussed areas, but is very hard to represent in C# because it would require a hard guarantee that a struct with a
`ref` field is truly never defaulted, by anything. `ref`s do not have a "default", so a struct that contained on in a field would
need to not be possible to default in any fashion. This proposal could overlap with that feature: the guarantees provided here are
no stronger than the guarantees given with nullable reference types, which is to say easy to break: arrays of these structs would
still be filled with `default` on creation, for example, even if the type wasn't annotated with a `??` or hypothetical other sigil.
We need to be sure that, if that's the case and we do want to add `ref` fields, we're comfortable having both a "soft" and "hard"
defaultness guarantee in the language.
Finally, there was some recognition and discussion around how this issue is very similar to another long standing request from
libraries like Unity: custom nullability. The idea is that with C#, among the entire value domain of a type we recognize and have
built language support for one _particular_ invalid value: `null`. However, this isn't the only invalid value that a value domain
may have. Unity objects have backing C++ instances, and they override the `==` operator to allow comparison with `null` to also
return true if the backing field has not yet been instantiated. While the C# object itself is not `null` in this case, it _is_
invalid, and should be treated as such. However, this doesn't play well with other operators in C#, such as `?.`, `??`, and
`is null`. These all special-case a particular invalid value, `null`, and don't play well with other invalid values, leading
libraries like Unity to encourage users to write code that does not take advantage of modern idiomatic C# features. This issue is
very similar to the non-defaultable structs issue: we'd like to recognize a particular value in the domain of a struct type as
invalid. It might be better to implement this as a general invalid value pattern that any type, struct or class, can opt into.
#### Conclusion
For both of these issues, we need to take more time and rethink them again, especially in light of the removal of `??`, which
the non-defaultable struct type proposal relied on heavily. A small group will explore the space more, particularly the more
general invalid object pattern, and come back with a rethought proposal. The guiding principle that this group should keep in
mind from the current proposal is "Users should be able to change something from a class to a struct for performance without
significant redesign due to having to handle an invalid `default` struct value."
### Confirming unspeakable `Clone` method implications
Before we ship unspeakable `Clone` methods for `with` expressions, we wanted to make sure that we've worked through the
consequences of doing so, and are sure that the language will be able to continue to evolve without breaking scenarios that
we are enabling with this feature. In particular, in the face of a general factory pattern that users can use to extend record
types, or even potentially expand what is today a record type into a full blown type without breaking their customers, we
might need to emit both the unspeakable `Clone` method and a factory method in the future. A guiding principle for record design
has been that whether something is a record is an implementation detail. Therefore whatever future method we add that will allow
a regular class type to participate in a `with` expression will likely have to emit this method as well.
We also considered whether we should take any measures right now to try and keep our design space open in records for adding a
user-overridable `Clone` method. We could try emitting the method now, and modreq it so that it cannot be directly called from
C# code, or we could just block users from creating a `Clone` method in a record entirely.
#### Conclusion
We're fine with the unspeakable name being a feature of records forever going forward. We will also reserve `Clone` as a member
name in records to ensure that our future selves will be able to design in this space.

View file

@ -1,126 +0,0 @@
# C# Language Design Meeting for July 6th, 2020
## Agenda
1. [Repeated attributes on `partial` members](#repeated-attributes-on-partial-members)
2. [`sealed` on `data` members](#sealed-on-data-members)
3. [Required properties](#required-properties)
## Quote of the Day
"Is there a rubber stamp icon I can use here?"
## Discussion
### Repeated attributes on `partial` members
https://github.com/dotnet/csharplang/pull/3646
Today, when we encounter attribute definitions among partial member definitions, we merge these attributes, applying attributes multiple
times if there are duplicates across the definitions. However, if there are duplicated attributes that do not allow multiples, this merging
results in an error that might not be resolvable by the user. For example, a source generator might copy over the nullable-related
attributes from the stub side to the implementation side. This is further exacerbated by the new partial method model: previously, the
primary generator of partial stubs was the code generator itself. WPF or other code generators would generate the partial stub, which the
user could then implement to actually create the implementation. These generators generally wouldn't add any attributes, and the user could
add whatever attributes they wanted. However, the model is flipped for the newer source generators. Users will put attributes on the stub in
order to hint to the generator how to actually implement the method, so either the generator will have never copy attributes over (possibly
complicating implementation), or it will have to be smart about only copying over attributes that matter. It would further hurt debugability
as well; presumably tooling will want to show the actual implementation of the method when debugging, and it's likely that the tooling won't
want to try and compute the merged set of attributes from the stub and the implementation to show in debugging.
What emerged from this discussion is a clear divide in how members of the LDT view the stub and the implementation of a partial member: some
members view the stub as a hint that something like this method exists, and the implementation provides the final source of truth. This group
would expect that, if we were designing again, all attributes would need to be copied to the implementation and attributes on the stub would
effectively be ignored. The other segment of the LDT viewed partial methods in exactly the opposite way: the stub is the source of truth, and
the implementation had better conform to the stub. This reflects these two worlds of previous source generators vs current generators: for the
previous uses of partial, the user would actually be creating the implementation, so that's the source of truth. For the new uses, the user is
creating the stubs, so that's the source of truth.
We also briefly considered a few ways of enabling the attribute that keys the generator to be removed from the compiled binary, so that it
does not bloat the metadata. However, we feel that that's a broader feature that's been on the backlog for a while, source-only attributes. We
don't see anything in this feature conflicting with source-only attributes. We also don't see anything in this feature conflicting with
future expansions to partial members, such as partial properties.
#### Conclusion
The proposal is accepted. For the two open questions:
1. We will deduplicate across partial types as well as partial methods if `AllowMultiple` is false. This is considered lower priority if a
feature needs to be cut from the C# 9 timeframe.
2. We don't have a good use-case for enabling `AllowMultiple = true` attributes to opt into deduplication. If we encounter scenarios where
this is needed, we can take it up again at that point.
### `sealed` on `data` members
In a [previous LDM](LDM-2020-06-22.md#data-properties), we allowed an explicit set of attributes on `data` members, but did not include
`sealed` in that list, despite allowing `new`, `override`, `virtual`, and `abstract`. `sealed` feels like it's missing, as it's also
pertaining to inheritance.
#### Conclusion
`sealed` will be allowed on `data` properties.
### Required properties
https://github.com/dotnet/csharplang/issues/3630
In C# 9, we'll be shipping a set of improvements to nominal construction scenarios. These will allow creation of immutable objects via
object initializers, which has some advantages over positional object creation, such as not requiring exponential constructor growth
over object hierarchies and the ability to add a property to a base class without breaking all derived types. However, we're still
missing one major feature that positional constructors and methods have: requiredness. In C# today, there is no way to express that a
property must be set by a consumer, rather than by the class creator. In fact, there is no requirement that all fields of a class type
need to be initialized during object creation: any that aren't initialized are defaulted today. The nullable feature will issue warnings
for initialized fields inside the declaration of a class, but there is no way to indicate to the feature that this field must be initialized
by the consumer of the class. This goes further than just the newly added `init`: mutable properties should be able to participate in this
feature as well. In order for staged initialization to feel like a true first-class citizen in the language, we need to support requiredness
in the contract of creating a class via the feature.
The LDM has seemingly universal support of making improvements here. In particular, the proposed concept of "initialization debt" resonated
strongly with members. It allows for a general purpose mechanism that seems like it will extend natural to differing initialization modes.
We categorized the two extreme ends of object initialization, both of which can easily be present in a single nominal record: Nothing is
initialized in the constructor (the default constructor), and everything is initialized in the constructor (the copy constructor). The next
question is how are these initialization contracts created: there's some tension here with the initial goal of nominal construction.
Fundamentally, initialization contracts can be derived in one of two ways: implicitly, or explicitly. Implicit contracts are attractive at
first glance: they require little typing, and importantly they're not brittle to the addition of new properties on a base class, which was
an important goal of nominal creation in the first place. However, they also have some serious downsides: In C# today, public contracts for
consumers are always explicit. We don't have inferred field types or public-facing parameter/return types on methods/properties. This means
that any changes to the public contract of an object are obvious when reviewing changes to that type. Implicit contracts change that. It
would be very possible for a manually-implemented copy constructor on a derived type to miss adding a copy when a property is added to its
base type, and suddenly all uses of `with` on that type are now broken.
We further observe that this shouldn't just be limited to auto-properties: a non-autoprop should be able to be marked as required, and then
any fields that the initer or setter for that property initializes can be considered part of the "constructor body" for the purposes of
determining if a field has been initialized. Fields should be able to be required as well. This could extend well to structs: currently,
struct constructors are required to set all fields. If they can instead mark a field or property as required then the constructor wouldn't
have to initialize it all.
One way of implementing initialization debt would be to tie it to nullable: it already warns about lack of initialization for not null
fields when the feature is turned on. We're still in the nullable adoption phase where we have more leeway on changing warnings, so we
could actually change the warning to warn on _all_ fields, nullable or not. This would effectively be an expansion of definite assignment:
locals must be assigned a value before use, even if that value is the default. By extending that requirement to all fields in a class, we
could essentially make all fields required when nullable is enabled, regardless of their type. Then, C# 10 could add a feature to enable
skipping the initialization of some members based on whether the consumer must initialize them. This is also not really a breaking change
for structs: they're already required to initialize all fields in the constructor. However, it would be a breaking change for classes, and
we worry it would be a significantly painful one, especially with no ability to ship another preview before C# 9 releases. `= null!;` is
already a significant pain point in the community, and this would only make it worse.
We came up with a few different ways to mark a property as being required:
* A keyword, as in the initial proposal, on individual properties.
* Assigning some invalid value to the field/property. This could work well as a way to be explicit about what fields a particular
constructor would require, but does leave the issue about inherited brittleness.
* Attributes or other syntax on constructor bodies to indicate required properties.
We like the idea of some indication on a property itself dictating the requiredness. This puts all important parts of a declaration together,
enhancing readability. We think this can be combined with a defaulting mechanism: the property sets whether it is required, and then a
constructor can have a set of levers to override individual properties. These levers could go in multiple ways: a copy constructor could
say that it initializes _all_ members, without having to name individual members, whereas a constructor that initializes one or two specific
members could say it only initializes those specific members, and inherits the property defaults from their declarations. There's still
open questions in this proposal, but it's a promising first start.
#### Conclusion
We have unified consensus that in order for staged initialization to truly feel first-class in the language, we need a solution to this issue,
but we don't have anything concrete enough yet to make real decisions. A small group will move forward with brainstorming and come back to
LDM with a fully-fleshed-out proposal for consideration.

View file

@ -1,213 +0,0 @@
# C# Language Design Meeting for July 13th, 2020
## Agenda
1. [Generics and generic type parameters in aliases](#Generics-and-generic-type-parameters-in-aliases)
2. ["closed" enum types](#"closed"-enum-types)
3. [Allow `ref` assignment for switch expressions](#Allow-`ref`-assignment-for-switch-expressions)
4. [Null suppressions nested under propagation operators](#Null-suppressions-nested-under-propagation-operators)
5. [Relax rules for trailing whitespaces in format specifier](#Relax-rules-for-trailing-whitespaces-in-format-specifier)
6. [Private field consideration in structs during definite assignment analysis](#Private-field-consideration-in-structs-during-definite-assignment-analysis)
7. [List patterns](#list-patterns)
8. [Property-scoped fields and the `field` keyword](#Property-scoped-fields-and-the-field-keyword)
9. [File-scoped namespaces](#file-scoped-namespaces)
10. [Allowing `ref`/`out` on lambda parameters without explicit type](#Allowing-ref/out-on-lambda-parameters-without-explicit-type)
11. [Using declarations with a discard](#Using-declarations-with-a-discard)
12. [Allow null-conditional operator on the left hand side of an assignment](#Allow-null-conditional-operator-on-the-left-hand-side-of-an-assignment)
12. [Top level statements and functions](#Top-level-statements-and-functions)
13. [Implicit usings](#implicit-usings)
## Quote of the day
"It does actually seem like this pattern is the 98% case..."
"I just want to disagree with something [redacted] said... [they] said [they] thought it was the 98% case that this would apply to, and I think it's 99..."
"Not fair, I was going to say that."
## Discussion
This meeting was issue triage. There will not be much detail on any particular issue, just a general summary
of the LDM's feeling and the milestone that we triaged to.
### Generics and generic type parameters in aliases
https://github.com/dotnet/csharplang/issues/1239
In general, we want to make improvements in this area. We should also be open to making other enhancements
in this space, such as allowing tuple syntax on the right side, or allowing C# keywords like `int`. There's
also a set of features that we should investigate in parallel, such as globally-visible aliases or publicly-
exported aliases.
#### Conclusion
Triaged for C# 10 for now so that we can look at the space hollistically in the near term.
### "closed" enum types
https://github.com/dotnet/csharplang/issues/3179
This is a solution to the very common complaints around enum usage, particularly in switch expressions and other
exhaustiveness scenarios. It will also work well with DUs, and we should make sure that whatever syntax we use
there works well with closed or sealed enum types as well.
#### Conclusion
Triaged for C# 10 to look at in coordination with DUs.
### Allow `ref` assignment for switch expressions
https://github.com/dotnet/csharplang/issues/3326
We like this proposal. You can already do this with ternaries, and it's odd that you can't do this with switch
expressions as well. It's not high priority, but we'd be happy to see it in the language. Like ternaries, the
ref version would not be target-typed.
#### Conclusion
Triaged for Any Time.
### Null suppressions nested under propagation operators
https://github.com/dotnet/csharplang/issues/3393
This will be a breaking change, so we need to get it in as soon as possible.
#### Conclusion
C# 9 timeframe. We need to make a syntax proposal and approve it.
### Relax rules for trailing whitespaces in format specifier
https://github.com/dotnet/csharplang/issues/3414
In reading this proposal, we're unsure whether the request was for _ignoring_ trailing spaces in format specifiers,
or allowing them and including them in the format specifier. We think that either way this is interpreted, it will
be confusing: trailing spaces are allowed in front of an interpolated expression and mean nothing, but for things
like `DateTime`, spaces are valid parts of a format specifier and will be respected in the output. We think that
either behavior here would be confusing, with no clear indication on which the user wants.
#### Conclusion
Rejected.
### Private field consideration in structs during definite assignment analysis
https://github.com/dotnet/csharplang/issues/3431
This is a bug from the native compiler that had to be reimplemented in Roslyn for compat, but it's always been
one of the prime candidates for a warning wave (and is already supported as an error in the compiler via the strict
feature flag). There is some contention whether it should be a warning or an error in this wave. There is also a
concern that this will combine with the `SkipLocalsInit` feature in C# 9 to expose an uninitialized memory hole in
C# with no use of `unsafe` or `System.Unsafe` required.
#### Conclusion
We need to look at this ASAP to make sure that we don't unintentionally expose unsafety in the language without
user intention. We'll schedule this for next week.
### Remove restriction that optional parameters must be trailing
https://github.com/dotnet/csharplang/issues/3434
It's possible to define such methods in metadata with the correct attribute usage _and_ call them from C# today.
This would just be about removing the restriction from _defining_ them in C#.
#### Conclusion
Triaged for Any Time.
### List patterns
https://github.com/dotnet/csharplang/issues/3435
We have an open proposal that with a prototype. We like the general direction it takes, but we need to have more
detailed design review of it and possibly make a few different decisions. This is a direction we want to take
pattern matching in future releases, so we'll continue iterating on this.
#### Conclusion
Triaged into C# 10 for design work.
### Property-scoped fields and the `field` keyword
* https://github.com/dotnet/csharplang/issues/133
* https://github.com/dotnet/csharplang/issues/140
We've started doing design work around both of these issues. We should continue doing that.
#### Conclusion
Triaged into C# 10.
### File-scoped namespaces
https://github.com/dotnet/csharplang/issues/137
This has been a large request for a long time that reflects the default of almost every single C# file written.
There's still some design work to do, particularly in how that will affect `using` statements, but it's work
that we should take on.
#### Conclusion
Triaged into C# 10.
### Allowing `ref`/`out` on lambda parameters without explicit type
https://github.com/dotnet/csharplang/issues/338
We've heard this request a few times, and while we don't see any particular issue with this, it's not a priority
for the team at this point. If a spec/implementation was provided we'd likely accept it but won't go out of our
way to add it unless something else changes here.
#### Conclusion
Triaged to Any Time.
### Using declarations with a discard
https://github.com/dotnet/csharplang/issues/2235
There's still some open design questions here, particularly around possibly ambiguous syntaxes. We need to keep
iterating on this to find a design that we like.
#### Conclusion
Triaged for X.X, pending a new proposal.
### Allow null-conditional operator on the left hand side of an assignment
https://github.com/dotnet/csharplang/issues/2883
Initial discussion shows a big LDT split on whether `?.` should be able to effectively a conditional ref. We
would need to discuss further, but aren't ready to outright approve or reject the feature.
#### Conclusion
Triage to X.X for future discussion.
### Top level statements and functions
https://github.com/dotnet/csharplang/issues/3117
This is part 2 of the work we started in C# 9 with top-level statements: designing free-floating functions that
can sit on the top level without any containing type. We need to continue examining this context of scripting
unification, which we plan to continue doing on an ongoing basis.
#### Conclusion
Triage into C# 10 for ongoing discussion.
### Implicit usings
https://github.com/dotnet/csharplang/issues/3428
This is another issue arising of discussions on top-level statements and using C# as a scripting language:
attempting to make the language more brief by specifying common imports on the command line or as part of
the project file is certainly one way to remove boilerplate. However, it's a controversial feature that often
draws visceral reactions. We need to discuss this more and figure out if it's a feature we want to have in C#,
taking into account our increased focus on scripting-like scenarios. It's worth noting that CSX has a limited
form of this support already, and implementing this in C# proper would bring us one step closer to unifying the
two dialects.
#### Conclusion
Triage to C# 10 for discussion.

View file

@ -1,113 +0,0 @@
# C# Language Design Meeting for July 20th, 2020
## Agenda
1. [Finishing Triage](#finishing-triage)
a. [Extend with expression to anonymous type](#Extend-with-expression-to-anonymous-type)
b. [Required properties](#Required-properties)
c. [Shebang support](#shebang-support)
2. [Private reference fields in structs with `SkipLocalsInit`](#Private-reference-fields-in-structs-with-`SkipLocalsInit`)
## Quote of the Day
"If you go get a beer from the fridge or wherever you keep them and drink it right now, I'll refund you."
## Discussion
### Finishing Triage
#### Extend with expression to anonymous type
https://github.com/dotnet/csharplang/issues/3530
We don't see any reason why we couldn't extend `with` expressions to handle anonymous types, and we universally support
the idea. We do see room for further improvement as well. Struct types should be generally possible to `with`, and in
particular tuples should feel first class here. Some other ideas for future improvements that we'll need to keep in mind
when designing anonymous types is an ability to specify a `withable` constraint: perhaps that's a thing we could do via
typeclasses, but if that's a thing that should be specifiable then we'll want to make sure that whatever we do for structs
and anonymous types works well with it.
##### Conclusion
Triaged into C# 10 for discussion with the other `with` enhancements
#### Required properties
https://github.com/dotnet/csharplang/issues/3630
A smaller group is currently fleshing this proposal out in the direction of initializer debt, and is looking to talk more
about it in the next few months.
##### Conclusion
Triaged for C# 10.
#### Shebang support
https://github.com/dotnet/csharplang/issues/3507
This is part of the next step in the C# scripting discussion. In particular, this proposal details just one, very small
piece of the puzzle here, namely the ability have a C# file startup an environment. There is significantly more work to
be done in the language and tooling to effectively modernize the loose-files support for C#. Today, C# code has a core
concept that cs files themselves do not contain information about the runtime environment. They don't specify the target
runtime, nuget dependencies, where tools can be found, or anything else similar (beyond some `#ifdef` blocks). Supporting
this would be intentionally eroding this aspect, which we need to make sure we're doing with intention and design. There
is support among LDT members for this scenario, so we'll continue to look at.
##### Conclusion
Triaged into C# 10 for discussion. We acknowledge that we may well not ship anything in this space as part of C# 10, but
we want to start discussing possible futures here in the coming X months, not X years.
### Private fields in structs with `SkipLocalsInit`
https://github.com/dotnet/csharplang/issues/3431
Proposal 1: https://github.com/dotnet/roslyn/issues/30194#issuecomment-657858716
Proposal 2: https://github.com/dotnet/roslyn/issues/30194#issuecomment-657900257
This topic was brought up last week when we realized there was a potential hole in `SkipLocalsInit` with definite
assignment, where we realized it's possible to observe uninitialized memory via private fields. When importing metadata,
the native compiler ignored private fields, including private fields in structs, and did not require them to be considered
definitely assigned before usage. This behavior was preserved when we implemented Roslyn, as attempting to break it was
too large of a change for organizations to adopt. Both of these proposals ensure that, with `SkipLocalsInit` on a method,
it's considered an error to not definitely assign here, which was fine with LDM as this is a new feature and cannot break
anyone. We then looked at the differences between the two proposals, namely whether the definite assignment diagnostic
should be a warning or an error when users opt into to a new warning wave. We found the arguments around incremental
adoption compelling: we don't want users to be blocked off from adopting new warning waves and making their code at least
a little safer by issuing errors that cannot be ignored. If users want to ensure that these warnings are fixed in their
code, they can use `warnAsError` to turn these specific warnings or all warnings into errors, as you can today.
#### Conclusion
Proposal 1 has been chosen:
1. If a method has the [SkipLocalsInit] attribute, or the compilation has the "strict" feature enabled, we use the strong
version of analysis for its body. Otherwise
2. If a sufficiently high /warnversion (>= 5) is enabled, we run the strong version of analysis, and
a. If it reports no diagnostics, we are done.
b. If it reports diagnostics, we run the weak version of analysis. Errors that are common to the two runs are reported
as errors, and errors only reported by the strong version are downgraded to a warning.
3. Otherwise we run the weak version of analysis.
### Future record direction
See https://github.com/dotnet/csharplang/issues/3707 for the full list. We briefly went through the list here to gauge
support from LDT members on the various proposals. We didn't get particularly in depth on any one part, but some
highlights:
* `init` fields and methods - We rejected these in 9, deciding to wait for community feedback. We believe this is still
the right decision, and want to hear user scenarios before proceeding further.
* Final initializers - We briefly entertained the idea of having an `init { }` syntax to guarantee that a block is called
after a method. This was later rejected as too complicated after design review. Like `init`, we need to get a better
handle on the user scenarios.
* Required members - We have broad support for doing something in this space.
* Factores - We have broad support for doing something in this space.
* User-defined cloning - Will likely need to be designed hand-in-hand with factories
* Cross-inheritance between records and non-records - part of the initial record goal was that `record` is pure syntactic
sugar. If that's still a goal, then we need to do more work here.
* Generalized primary constructors - Mixed support here. We need to do more design work.
* Primary constructor bodies - Might be done for the former. We need to flesh this out so we can determine how to apply
attributes to record constructors.
* Discriminated unions - We need to determine whether this will be a simple syntactic shorthand for a general sealed type
hierarchy feature, or if this will be the only way to declare such hierarchies.

View file

@ -1,186 +0,0 @@
# C# Language Design Meeting for July 27th, 2020
## Agenda
- [Improved nullable analysis in constructors](#Improved-nullable-analysis-in-constructors)
- [Equality operators in records](#Equality-operators-in-records)
- [`.ToString()` or `GetDebuggerDisplay()` on records](#ToString-or-DebuggerDisplay-on-record-types)
- [W-warnings for `T t = default;`](#W-warnings-for-`T-t-=-default;`)
## Quote of the Day
"We should pick our poison: one is arsenic, and will kill us. The other is a sweet champagne, and will give us a headache tomorrow."
![Delectable Tea or Deadly Poison](delectable_tea_2020_07_27.png)
## Procedural Note
LDM is going on August hiatus as various members will be out. We'll meet next on August 24th, 2020.
## Discussion
### Improved nullable analysis in constructors
https://github.com/dotnet/csharplang/blob/master/proposals/nullable-constructor-analysis.md
This proposal is a tweak to the nullable warnings we emit today in constructors that would bring these warnings more in line with
behavior for `MemberNotNull`, as well as addressing a few surprising cases where you do not get a deserved nullable warning today.
This code, for example, has no warnings today:
```cs
public class C
{
public string Prop { get; set; }
public C() // we get no "uninitialized member" warning, as expected
{
Prop.ToString(); // unexpectedly, there is no "possible null receiver" warning here
Prop = "";
}
}
```
We consider this to be possible to retrofit into the nullable feature, as we're still within the nullable adoption phase. After
C# 9 is released, however, this would be a breaking change, so we will need to either do it now, or do it in a warning wave
(which would heavily complicate the implementation). Additionally, even though we're still within the nullable adoption phase,
this is a relatively big change in warnings that could result in lots of new errors in codebases that have heavily adopted nullable.
We also considered whether there could be any interference here with C# 10 plans around required properties or initialization debt.
There does not appear to be, as this would effectively be the initialization debt world where a constructor is required to set all
properties in the body, and any relaxation of this by requiring properties to be initialized at construction site will play well.
#### Conclusion:
We should implement the change and smoke test it on roslyn, the runtime repo, and on VS. If there is large churn, we'll re-evaluate
at that point.
### Equality operators in records
https://github.com/dotnet/csharplang/issues/3707#issuecomment-661800278
We've talked briefly about equality operators in records prior to now, but we never ended up making any firm decisions, so now we
need to make an intentional decision about records before we ship as doing so after would be a breaking change. The concern is that,
because class types inherit a default equality operator from simply being a class, we'll get into a scenario where a record's `Equals`
method could return `true`, and the `==` operator returns false. Most standard .NET "values" behave in this fashion as well: `string`
being the most common example.
There are some interesting niggles, however, particularly with `float` and `double` values. For example,
this code:
```cs
var a = (float.NaN, double.NaN);
System.Console.WriteLine(a == a); // Prints false
System.Console.WriteLine(a.Equals(a)); // Prints true
```
This happens because the `==` operator and the `Equals` method for floats and doubles behave differently. The `==` operator uses IEEE
equality, which does not consider `NaN` equal to itself, while `Equals` uses the CLR definition of equality, which does. When combined
with `ValueTuple`, this makes for an interesting set of behaviors, as `ValueTuple` doesn't have its own definition of the `==` operator.
Rather, the compiler synthesizes the set of `==` operators on the component elements of the tuple. This means that for generic cases,
such as `public void M<T>((T, T) tuple) { _ = tuple == tuple; }`, you get an error when attempting to use `==` on the tuple, since `==`
is not defined on generic type parameters. `ValueTuple` is a particularly special case here though. The compiler special cases knowledge
of the implementation, which is not generalizable across inheritance hierarchies with record types. In order for an implementation to do
memberwise `==` across all levels of a record type, there will have to be hidden virtual methods to take care of this, and it gets more
complicated when you consider that a derived record type could introduce a generic type parameter field that can't be `==`'d. We feel
that attempting to get too clever here would end up being unexplainable, so if we want to implement the operators, it should just
delegate to the virtual `Equals` methods we already set up.
Next, we considered whether we should implement `==` operators on all levels of a record inheritance hierarchy, or just the top level.
With records as they're shipping in C# 9, we don't believe that there's a scenario you can get into that would break this. However, the
eventual goal is to enable records and classes to inherit from each other, and that point if every level didn't provide its own
implementation it could end up being possible to break assumptions. Further, attempting to trim implementations of `==` and `!=` would
end up resulting in complicated rules that don't feel necessary: adding the operators isn't going to bloat metadata size, will potentially
allow eliminating of some levels of equality method calls, and future proofs ourselves.
Finally, we looked at whether the user will be able to provide their own implementation of `==` and `!=` operators, and if we'd error
on this or allow it and not generate the operators ourselves in this case. We feel that allowing this would complicate records: there
are existing extension points into record equality that can be overridden as needed, and we want to have a stronger concept of `Equals`
and `==` being the same. If there are good user scenarios around allowing this, we can consider it at a later point.
#### Conclusion
We will generate the `==` and `!=` operators on every level of a record hierarchy. These implementations will not be user-overridable,
and they will delegate to `.Equals`. The implementation will have this semantic meaning:
```cs
pubic static bool operator==(R? one, R? other)
=> (object)one == other || (one?.Equals(other) ?? false);
public static bool operator!=(R? one, R? other)
=> !(one == other);
```
### `ToString` or `DebuggerDisplay` on record types
#### Initial proposal
1. `record` generates a ToString that prints out its type name followed by a positional syntax: Point(X: 1, Y: 2).
(issue: disfavors nominal records)
2. `record` generates a ToString that does the above and also appends nominal properties by calling ToStringNominal:
`FirstName: First, LastName: Last`, and assembles the parts into `Person { FirstName: First, LastName: Last, ID: 42 }`.
#### Discussion
The initial proposal here is to add a `ToString` method that would format a record type by their positional properties, and have a
separate method for creating a string from nominal properties named something like `ToStringNominal`. The initial reaction was that
this seemed overly complicated: there should just be one method that does "the right thing<sup>tm</sup>". There's further question
about whether having a `ToString` or `DebuggerDisplay` would be useful: depending on the properties of the record type, it could end
up doing more harm than good (if the properties were lazy, for example). Additionally, when a `ToString` is provided it's often domain
specific, and nothing we do in the compiler would be able to predict what shape that should be. We ended up coming up with a list of
common scenarios in which a `ToString` would be useful:
* Viewing in the debugger. This could be done with just a `DebuggerDisplay` attribute, or users can just expand the object to view the
properties of it as you can today.
* Viewing in test output. Using an equality test, for example, will usually print the objects if they were not equal to each other,
and it would be useful if that was actually meaningful, particularly for these value-like class types.
* Logging output. This is a very similar case to the previous.
We also considered whether we should implement this via source generators. However, it feels very similar to equality: we have a
sensible default there, and anyone who wants to customize (this field should be excluded, this one should use sequence equality)
can either write it manually or use a source generator. The same arguments apply here: we can provide a sensible default that will
enable short syntax, and anyone who wants to get custom behavior can override this.
After considering _whether_ to do this feature, we looked at what properties would be included in such a feature. How, would they
be formatted, and which ones are considered? The initial proposal was for positional only, with a separate method for nominal
properties. We felt that this was too complicated and likely not to be the right defaults: it totally disadvantages nominal records
and makes the implementation more complicated. The options we considered are:
* The same members as .Equals. This means all fields of a class, translating auto-generated backing fields to their properties for
display names. This would make explaining what appears in the `ToString` simple: it's the equality members, full stop.
* The positional members and `data` members of a record. We feel that this is too restrictive, especially since `data` members will
not be shipping in final form with C# 9: they'll still be under the preview language version.
* The fields and properties of a record type with some accessibility, accessibility to be determined next. We believe that it's very
unusual for a `ToString` to display `private` fields, which the other proposal would do.
We leaned towards just doing the fields and properties of a type, with some accessibility. Finally, we looked at what accessibility
to use by default for these:
* All members not marked `private`. This means that things that not relevant to a consumer of the type, such as `protected` fields,
show up in the `ToString`, and violates encapsulation principles.
* All `public` and `internal` members. This propsal has some of the same issues as the previous one: you could making an `internal`
type that implements a `public` interface, and it would be odd if the final user sees that internal state.
* At least as accessible as the record itself. This would mean that if the `record` was `internal`, then internal members would be
shown. If the record was `public`, then only `public` members would be shown. This doesn't solve any of our issues with the previous
proposal, and adds a behavioral difference between making a type `public` or `internal`.
* Only `public` members. This is what the positional constructor will generate by default, and it feels like the most natural state
to choose. This ensures that encapsulation isn't violated, and records with lots of `internal` or `private` state that want to
display these in a `ToString` are likely not really records anyway. If the user really wants to change this, they can customize it
as they choose.
Conclusion:
We'll have a generated `ToString`, that will display the public properties and fields of a `record` type. We'll come back with a
specific proposal on how this will be implemented at a later point.
### W-warnings for `T t = default;`
Just after we initially shipped C# 8, we made a change to warn whenever `default` was assigned to a local or property of type `T`,
or when a default was cast to that type. There was no way to silence this warning without using a `!`, so we reverted the change.
However, now that we are shipping `T?`, there is an actual syntax that users can write in order to express "this location might
be assigned default". We know from experience that this is something that users already do a lot, so making a change here would be
pretty breaking, even for the nullable rollout phase of the first year. Further, as we know that if the `!` is the only way to get
rid of this warning then we'll get lots of feedback from users, we believe that we can't unconditionally warn here: you can only
get rid of the warning in C# 9, so at a minimum it should only be reported in C# 9. We could also put this in a warning wave, and
you would only get it when you opt into the warning wave. An important thing to consider with doing this as a warning wave implies
that it should be separate error code from 8600: we've previously told people that if they don't want to annotate all their local
variables with `?`s, then they should disable that warning and all the rest of the warnings will be actual safety warnings. This
would add another warning to that list to disable. We could make it memorable as well, but one of the key aspects in warning waves
is that you should be able to opt out of individual warnings if necessary, so users will need to be able to opt out of this new
warning without turning off all the existing 8600 warnings. All that being said, there's also a serious backcompat concern here,
as introducing the warning when upgrading to C# 9 under the existing error code could cause people to hold off on upgrading at all.
#### Conclusion
We'll add a new warning here, under a warning wave.

View file

@ -1,196 +0,0 @@
# C# Language Design Meeting for July 27th, 2020
## Agenda
- [Warnings on types named `record`](#warnings-on-types-named-record)
- [`base` calls on parameterless `record`s](#base-calls-on-parameterless-records)
- [Omitting unnecessary synthesized `record` members](#omitting-unnecessary-synthesized-record-members)
- [`record` `ToString` behavior review](#record-tostring-behavior-review)
- [Behavior of trailing commas](#behavior-of-trailing-commas)
- [Handling stack overflows](#handling-stack-overflows)
- [Should we omit the implementation of `ToString` on `abstract` records](#should-we-omit-the-implementation-of-tostring-on-abstract-records)
- [Should we call `ToString` prior to `StringBuilder.Append` on value types](#should-we-call-tostring-prior-to-stringbuilder.append-on-value-types)
- [Should we try and avoid the double-space in an empty record](#should-we-try-and-avoid-the-double-space-in-an-empty-record)
- [Should we try and make the typename header print more economic](#should-we-try-and-make-the-typename-header-print-more-economic)
- [Reference equality short circuiting](#reference-equality-short-circuiting)
## Quote of the Day
"He was hung up on his principles!"
"I painted my tower all ivory."
## Discussion
### Warnings on types named `record`
We previously discussed warning in C# 9 when a type is named `record`, as it will need to be escaped in field
definitions in order to parse correctly. This is a breaking change being made in C# 9, but we didn't explicitly
record the outcome of the warning discussion. We also discussed going further here: we could make lower-cased
type names a warning with the next warning wave, under the assumption that lowercase names make good keywords.
For example, we could start warning on types named `async` or `var`. Those who want to forbid usage of those
features in their codebase have a solution in analyzers.
#### Conclusion
We will warn on types named `record` in C# 9. Further discussion will need to happen for lowercase type names
in general, as there are globalization considerations, and we feel that disallowing `record` might be a good
smoke test for reception to the idea in general.
### `base` calls on parameterless `record`s
We have a small hole in records today where this is not legal, and there is no workaround to making it legal:
```cs
record Base(int a);
record Derived : Base(1);
```
The spec as written today explicitly forbids this: "It is an error for a record to provide a `record_base`
`argument_list` if the `record_declaration` does not contain a `parameter_list`." However, there is good
reason to allow this, and while this design may have fallen out of the principles that were set out given
that a default constructor for a nominal record is not recognized as a primary constructor, this seems to
violate principles around generally making record types smaller. Further, with the spec as written today
it's not possible to work around this issue by giving `Derived` an empty parameter list, as parameter lists
are not permitted to be empty.
A solution to this problem is relatively straightforward: we make the default constructor of a nominal record
the primary constructor, if no other constructor was provided. This seems to mesh well with the existing rules,
and seems to work well with future plans for generalized primary constructors. We could consider 2 variants of
this: requiring empty parens on the type declaration in all cases, or only requiring them in cases where the
default constructor would otherwise be omitted (if another constructor was added in the `record` definition).
#### Conclusion
We'll allow empty parens as a constructor in C# 9. We're also interested in allowing the parens to be omitted
when a default constructor would otherwise be emitted, but if this doesn't make it in C# 9 due to time
constraints it should be fine to push back to v next.
### Omitting unnecessary synthesized `record` members
Some record members might be able to be omitted in certain scenarios if they're unneeded. For example, a
`sealed` `record` that derives from `System.Object` would not need an `EqualityContract` property, as it
can only have the one contract. We could also simplify the implementation of `ToString` if we know there
will be no child-types that need to call `PrintMembers`.
However, despite leading to simplified emitted code for these types, we're concerned that this will cause
other code to become more complicated. We'll need to have more complicated logic in the compiler to handle
these cases. Further, source generators and other tools that interact with records programmatically will
have to duplicate this logic if they need to interact with record equality or any other portion of code that
"simplified" by this mechanism. We further don't see a real concern for metadata bloat in this scenario,
as we aren't omitted fields from the record declaration.
#### Conclusion
There are likely more downsides than upsides to doing something different here. We'll continue to have this
be regular.
### `record` `ToString` behavior review
Now that we have a proposal and implementation for `ToString` on record types, we'll go over some standing
questions from initial implementation review. Prior art for this area in C# that we found relevant was
`ToString` on both tuples and anonymous types.
#### Behavior of trailing commas
There is a proposal to simplify the implementation of `ToString` by always printing trailing commas after
properties. This would remove the need for some bool tracking flags for whether a comma was printed as
part of a parent's `ToString` call. However, no prior art (in either C# or other languages) that we could
find does this. Further, it provokes a visceral reaction among team members as feeling unfinished, and that
it would be the first bug report we get after shipping it.
##### Conclusion
No trailing commas.
#### Handling stack overflows
There is some concern that `ToString` could overflow if a record type is recursive. While this is also true
of equality and hashcode methods on records, there is some concern that `ToString` might be a bit special
here, as it's the tool that a developer would use to get a print-out of the record to find the cyclic element
in the first place. There's examples in the javascript world of this being a pain point. However, any
solution that we come up with for records will have limitations: it wouldn't be able to protect against
indirect cycles through properties that are records but not records of the same type, or against cycles
in properties that are not records at all. In general, if a user makes a record type with a cyclic data
structure, this is a problem they're going to have to confront already in equality and hashcode.
##### Conclusion
We will not attempt any special handling here. We could consider adding a `DebuggerDisplay` with more special,
reflective handling at a later point in time if this proves to be a pain point, but we don't think it's
worth the cost in regular `ToString` calls.
#### Quoting/escaping values
Today, records do not attempt to escape `string`s when printing, which could result in potentially confusing
printing. However, we do have concerns here. First, none of our prior art in C# does this escaping. Second,
the framework also does not escape `ToString()` like this. Third, there's a question of what _type_ of
escaping we should do here. Would we want to print a newlines as the C# version, `\n`, or the VB version?
Further, we already have some handling the expression evaluator and debugger for regular strings and
anonymous types, to ensure that the display looks good there. Given that this is mainly about ensuring
that records appear well-formatted in the debugger, it seems like the correct and language-agnostic approach
is to ensure the expression evaluator knows about record types as well.
##### Conclusion
We won't do any quoting or escaping of string record members. We should make the debugger better here.
#### Should we omit the implementation of `ToString` on `abstract` records
We could theoretically omit providing an implementation of this method on `abstract` record types as it
will never be called by the derived `ToString` implementation. On the face of it, we cannot think of a
scenario that would be particularly helped by including the `ToString`, however we also cannot think of
a scenario that would be harmed by including it, and doing so would make the compiler implementation simpler.
##### Conclusion
Keep it.
#### Should we call `ToString` prior to `StringBuilder.Append` on value types
The concern with directly calling `Append` without `ToString` on the value being passed is that for
non-primitive struct types, this will cause the struct to be boxed, and the behavior of `Append` is
to immediately call `ToString` and pass to the `string` overload.
##### Conclusion
Do it.
#### Should we try and avoid the double-space in an empty record
The implementation currently prints `Person { }` for an empty record. This provokes immediate "unfinished"
reactions in the LDT, similar to the trailing commas above.
##### Conclusion
Should we remove it? YES! YES! YES! YES!
#### Should we try and make the typename header print more economic
Today, we call `StringBuilder.Append(nameof(type))` and `StringBuilder.Append("{")` immediately after
as 2 separate calls, which is another method call we could drop if we did it all in one. We feel that this
is sufficiently outside the language spec as to be a thing a compiler could do if it feels it appropriate.
From the compiler perspective, however, it could actually be bad change, as it would increase the size of
the string table, which is known to be an issue in some programs, while getting rid of one extra method call
in a method not particularly designed to be as fast as possible in the first place.
##### Conclusion
Up to implementors. However, we don't believe it to be a good idea or worth any effort.
### Reference equality short circuiting
Today, we have a slight difference between the equality operators `==`/`!=` and the implementation of the
`Equals(record)` instance method, in that the former compare reference equality first, before delegating
to the `Equals` method. This ensures that `null` is equal to `null` as a very quick check, and ensures we
only have to compare one operand to `null` explicitly in the implementation of the operators. The `Equals`
instance member then does not check this condition. However, this means that the performance characteristics
of these two members can be very different, with the operators being an order of magnitude faster on even
small record types since it has to do potentially many more comparisons. The proposal, therefore, is to
check reference equality in the `Equals` instance member as well, as the first element.
#### Conclusion
We will do this. We'll keep the reference equality check in the operators as well to ensure that `null == null`,
and add one at the start of the instance method. It does mean that the `==` operators will check reference
equality twice, but this is an exceptionally quick check so we don't believe it's a big concern.

View file

@ -1,190 +0,0 @@
# C# Language Design Meeting for September 9th, 2020
## Agenda
1. [Triage issues still in C# 9.0 candidate](#triage-issues-still-in-C#-9.0-candidate)
2. [Triage issues in C# 10.0 candidate](#Triage-issues-in-C#-10.0-candidate)
# Quote(s) of the Day
"Don't even mention I was here."
"I have lots of strong opinions, I'm just judicious with how I distribute them."
# Discussion
## Triage issues still in C# 9.0 candidate
To start, we updated https://github.com/dotnet/csharplang/blob/master/Language-Version-History.md with the features that have been merged into the compiler for C# 9 at
this point, and moved their corresponding issues to the [9.0 milestone](https://github.com/dotnet/csharplang/milestone/18).
### Null propagation expression does not allow `!` as a nested operator form
https://github.com/dotnet/csharplang/issues/3393
We did not adequately prioritize this given the urgency of the breaking change. We need to attempt to get this into C# 9 or we may end up being stuck with this behavior
given that the nullable rollout period is ending, or we'd need to consider taking what could be a large breaking change. We'll attempt to throw and catch this hail mary,
and it will stay in C# 9.0 candidate for now.
### [Proposal] Improve overload resolution for delegate compatibility
https://github.com/dotnet/csharplang/issues/3277
We had hoped to get this done as a refactoring while implementing function pointers, but did not end up getting it in. This is a good candidate for community contribution
as it can be implemented and tested in a single PR, so we'll move this to any time.
### Interface static method, properties and event should ignore variance
https://github.com/dotnet/csharplang/issues/3275
This is implemented, just waiting on infrastructure to move forward to a runtime where it can be tested. Will be in C# 9 as soon as that happens, and is staying in 9.0
candidate.
### Records-related issues
https://github.com/dotnet/csharplang/issues/3226
https://github.com/dotnet/csharplang/issues/3213
https://github.com/dotnet/csharplang/issues/3137
These are all records related issues. We didn't get all of what we want to accomplish in this space in C# 9, so we need to break the still-to-be-done parts of these
proposals out into separate issues for 10 or later and close/move these.
### Primary Constructors
https://github.com/dotnet/csharplang/issues/2691
We didn't managed to break this out of records in a satisfactory manner for 9, but we would like to get this in for 10. Retriaged to 10.0 Candidate.
### Proposal: Target typed null coalescing (`??`) expression
https://github.com/dotnet/csharplang/issues/2473
When attempting to spec and implement this, we found that it doesn't actually work well in practice, as `??` has a number of complicated rules around type resolution
already and what part should be target-typed is confusing. We believe that, given the investigation, we won't be moving this one forward, and it moves to the Likely
Never milestone.
### Champion: Simplified parameter null validation code
https://github.com/dotnet/csharplang/issues/2145
We didn't quite get the code gen for this one finished up. Moving to 10.0 candidate.
### Champion: relax ordering constraints around `ref` and `partial` modifiers on type declarations
https://github.com/dotnet/csharplang/issues/946
We had put this in 9.0 because we thought it would be required for `data` classes. That did not end up being the case, and we don't have high priority for it otherwise.
We'd accept a contribution if a community member wanted to loosen the restriction, thus moved to Any Time.
### Champion "Nullable-enhanced common type"
https://github.com/dotnet/csharplang/issues/33
https://github.com/dotnet/csharplang/issues/881
We said when doing additional target-typing work that we either had to do these now, or it would be very complicated to implement later without avoiding breaking changes.
Given that the target-typing we added more generally addresses this in most scenarios, we don't believe that the additional "break glass in case of emergency" bar is
met with these, and they are moved to Likely Never.
### Proposal: improvements to nullable reference types
https://github.com/dotnet/csharplang/issues/3297
This is part of the ongoing nullable improvements list. We will split the parts that have not been implemented out into a separate list, and then move this to 9.0 with
just the things actually implemented for this release.
## Triage issues in C# 10.0 candidate
We will be moving issues from the 10.0 candidate bucket to the 10.0 Working Set bucket during triage. Issues in the 10.0 Working Set bucket are issues that we have decided
to devote some design time towards during the upcoming 10.0 timeframe, but not all of the issues triaged into this working set will actually make it into the 10.0 release.
### Proposal: Exponentiation operator
https://github.com/dotnet/csharplang/issues/2585
We don't feel that this is a major priority for the language currently. It is a relatively small amount of work, however. If a community member wants to add a proposal
for a specific symbol to use for the operator and a precedence for that symbol, we'd be happy to consider it at that point. Moved to Any Time.
### Champion "Type Classes (aka Concepts, Structural Generic Constraints)"
https://github.com/dotnet/csharplang/issues/110
We feel that issues in improving the type system, such as type classes, roles, statics in interfaces, etc, are the next big area of investment for the language. While
we may not end up seeing the results of these investments in the 10.0 timeframe, we need to start designing them now or we'll never see them at all. As such, we are
labeling this issue as "Long lead" to indicate that there is a lot of design work to come before and implementation can even start to happen. Moved to 10.0 Working Set.
### generic constraint: where T : ref struct
https://github.com/dotnet/csharplang/issues/1148
This is related to a forthcoming proposal on ref fields, as well as to the aforementioned type system improvements we want to make. To really be useful beyond allowing
`Span<Span<T>>`, ref structs need to be able to implement interfaces, and so this will be considered in tandem with them. Moved to 10.0 Working Set.
### Champion "CallerArgumentExpression"
https://github.com/dotnet/csharplang/issues/287
While this could be considered Any Time, we already have most of an implementation and would like to get it in for C# 10, so we are moving it to 10.0 Working Set.
### Champion "Allow Generic Attributes"
https://github.com/dotnet/csharplang/issues/124
A community member is contributing this feature, and while we don't think there is any design work left to do from the language side there's still a bit of work on the
implementation itself. We'll move this to 10.0 Working Set, as opposed to Any Time, to ensure that we give priority if there does end up being any language work to do.
### C# Feature Request: Allow value tuple deconstruction with default keyword
https://github.com/dotnet/csharplang/issues/1358
This is a small quality of life change that we already have a PR for. Let's get it merged, and move this issue to 10.0 Working Set.
### Expression Blocks/Switch Expression and Statement Enhancements
https://github.com/dotnet/csharplang/issues/3086
https://github.com/dotnet/csharplang/issues/3038
https://github.com/dotnet/csharplang/issues/3087
https://github.com/dotnet/csharplang/issues/2632
We know that we want to make some improvements here. We need to narrow down on a concrete proposal as we have a lot of different scopes/syntaxes currently, and then we
can proceed with implementation. Move to 10.0 Working Set.
### Champion: Name lookup for simple name when target type known
https://github.com/dotnet/csharplang/issues/2926
We consider this a somewhat essential necessary feature for discriminated unions, so this should be considered in concert with that. Move to 10.0 Working Set.
### Proposal: "Closed" type hierarchies
https://github.com/dotnet/csharplang/issues/485
This needs to be designed in concert with discriminated unions, so we'll move it to 10.0 Working Set.
### Championed: Target-typed implicit array creation expression `new[]`
https://github.com/dotnet/csharplang/issues/2701
This needs some design work around either making it a generalized feature for type parameter-based inference or if it specializes for the interfaces that array implements
specifically. We don't feel that this particularly pressing, so we're moving it to X.0 unless we hear a need to add it to align with other .NET priorities.
### Champion: `base(T)` phase two
https://github.com/dotnet/csharplang/issues/2337
While this was part of the initial feature set for DIMs, we don't hear any particular customer complaints for this feature. We'll move it to X.0 until such time as we
hear customer asks for it.
### Champion "defer statement"
https://github.com/dotnet/csharplang/issues/1398
LDM was somewhat negative on this feature. There are times when you'd want to pair code to run at the end of a block/method return, but defer has invisible consequences
here because it would have to use try/finally. This give defer a penalty that would prevent it from being used in many of the places we'd otherwise want to use it ourselves.
Additionally, there is significant negative community sentiment about this feature, much more than we usually get for participation on any particular csharplang issue. As a
result, we are moving this feature to Likely Never, and if we see similar significant outcry to the rejection we can reconsider this.
### Five ideas for improving working with fixed buffers
https://github.com/dotnet/csharplang/issues/1502
This will be part of the previously-mentioned forthcoming ref fields proposal. Move it to 10.0 Working Set.

View file

@ -1,111 +0,0 @@
# C# Language Design Meeting for September 14th, 2020
## Agenda
1. [Partial method signature matching](#partial-method-signature-matching)
2. [Null-conditional handling of the nullable suppression operator](#null-conditional-handling-of-the-nullable-suppression-operator)
3. [Annotating IEnumerable.Cast](#annotating-ienumerable.cast)
4. [Nullability warnings in user-written record code](#nullability-warnings-in-user-written-record-code)
5. [Tuple deconstruction mixed assignment and declaration](#tuple-deconstruction-mixed-assignment-and-declaration)
## Quote of the Day
- "Now with warning waves it's a foam-covered baseball bat to hit people with"
## Discussion
### Partial method signature matching
https://github.com/dotnet/roslyn/issues/45519
There is a question about what amount of signature matching is required for method signatures, both as part of the expanded partial methods in C# 9
and for the new `nint` feature in C# 9. Currently, our rules around what has to match and what does not are confusing: tuple names must match,
`dynamic`/`object` do not have to match, we warn when there are unsafe nullability conversions, and other differences are allowed (including parameter
names). We could lean on a warning wave here and make our implementation more consistent with the following general rules:
1. If there are differences in the CLR signature, we error (as we cannot emit code at all!)
2. If there are differences in the syntactic signature, we warn (even for safe nullability changes).
While we like this proposal in general, we have a couple of concerns around compatibility. Tuple names erroring is existing behavior, and if we loosen
that to a general warning that would need to be gated behind language version, as you could write code with a newer compiler in C# 8 mode that does not
compile with the C# 8 compiler. This complicates implementation, and to make it simple we lean towards just leaving tuple name differences as an error.
We also want to make sure that nullability is able to differ by obliviousness: a common case for generators is that either the original signature or the
implementation will be unaware of nullable, and we don't want to break this scenario such that either both the user and generator must be nullable aware,
or the must both be nullable unaware.
We also considered an extension to these rules where we make the new rules always apply to the new enhanced partial methods, regardless of warning level.
However, we believe that this would result in a complicated user experience and would make the mental model harder to understand.
#### Conclusion
1. We will keep the existing error that tuple names must match.
2. We will keep the existing warnings about unsafe nullability differences.
3. We will add a new warning wave warning for all other syntactic differences between partial method implementations.
a. This includes differences like parameter names and `dynamic`/`object`
b. This includes nullability differences where both contexts are nullable enabled, even if the difference is supposedly "safe" (accepting `null`
where it is not accepted today).
c. If nullability differs by enabled state (one part is enabled, the other part is disabled), this will be allowed without warning.
### Null-conditional handling of the nullable suppression operator
https://github.com/dotnet/csharplang/issues/3393
This is a spec bug that shipped with C# 8, where the `!` operator does not behave as a user would expect. Members of the LDT believe that this is broken
on the same level as the `for` iterator variable behavior that was changed in C# 5, and we believe that we should take a similar breaking change to fix
the behavior here. We have made a grammatical proposal for adjusting how null-conditional statements are parsed, and there was general agreement that this
proposal is where we want to go. The only comment is that `null_conditional_operations_no_suppression` should be renamed to avoid confusion, as there can
be a null suppression inside the term, just not at the end. A better name would be `null_conditional_operations_no_final_suppression`.
#### Conclusion
Accepted, with the above rename. Will get a compiler developer assigned to implement this ASAP.
### Annotating IEnumerable.Cast
https://github.com/dotnet/runtime/issues/40518
In .NET 5, the `Cast<TResult>` method was annotated on the return to return `IEnumerable<TResult?>`, which means that regardless of whether the input
enumerable can contain `null` elements, the returned enumerable would be considered to contain `null`s. This resulted in some spurious warnings when
upgrading roslyn to use a newer version of .NET 5. However, the C# in general lacks the ability to properly annotate this method for a combination of
reasons:
1. There is no way to express that the nullability of one type parameter depends on the nullability of another type parameter.
2. Even if there was a way to express 1, `Cast` is an extension method on `IEnumerable`, not `IEnumerable<T>`, because C# does not have partial type
inference to make writing code in this scenario better.
Given this, we have a few options:
1. Leave the method as is, and possibly enhance the compiler/language to know about this particular method. This is analogous to the changes we're
considering with `Where`, but it feels like a bad solution as it's not generalizable.
2. Make the method return `TResult`, unannotated. The issue with this is that it effectively means the method might actually lie: there is no way to
ensure that the method actually returns a non-null result if a non-null `TResult` is provided as a type, given that nullability is erased in the
implementation. We're concerned that this could make the docs appear to lie, which we think would also give a bad experience.
3. Convert `Cast` back to being unannotated. This seems to be compromise that both sides can agree on: analyzers can flag use of the unannotated API
to help users, and spurious warnings get suppressed. It also matches the behavior of `IEnumerator.Current`, and means that the behavior of `foreach`
loops over such a list behave in a consistent manner.
#### Conclusion
The BCL will make `Cast` and a few related APIs an oblivious API.
### Nullability warnings in user-written record code
The question here is on whether we should warn users when manually-implemented methods and properties for well-known members in a `record` should warn
when nullability is different. For example, if their `Equals(R other)` does not accept `null`. There was no debate on this.
#### Conclusion
we'll check user-defined `EqualityContract`, `Equals`, `Deconstruct`, ... methods on records for nullability safety issues in their declaration. For
example, `EqualityContract` should not return Type?.
### Tuple deconstruction mixed assignment and declaration
https://github.com/dotnet/csharplang/issues/125
We've discussed this feature in the past (https://github.com/dotnet/csharplang/blob/master/meetings/2016/LDM-2016-11-30.md#mixed-deconstruction), and
we liked it then but didn't think it would fit into C# 7. It's been in Any Time since, and now we have a community PR. We have no concerns with
moving forward with the feature.
#### Conclusion
Let's try and get this into C# 10.

View file

@ -1,66 +0,0 @@
# C# Language Design Meeting for September 14th, 2020
## Agenda
1. [Required Properties](#required-properties)
2. [Triage](#triage)
## Quote of the day
- "It's my version of thanks Obama, thanks C#"
## Discussion
### Required Properties
https://github.com/dotnet/csharplang/issues/3630
[Presentation](./Required_Properties_2020_09_16.pdf)
Most of the time today was used hearing a presentation about a proposal for required properties. This presentation was intended
as a high-level overview of the issue required properties are attempting to solve, and to illustrate a few approaches that could
be taken for implementing these. Notes were not taken to the slide content itself, as the slides are linked above.
Overall, the LDM is a bit leary about the implementation complexity of runtime-verification methods. One approach that wasn't on
the slides, but is a possible approach, is introducing validators and letting type authors do the verification themselves. This
may end up resulting in type authors conflating nullability and requiredness for most simple implementations, but for the common
scenarios this may be just fine. We were unable to find just one implementation strategy that stuck out as "this is the right thing
to do." Instead, each implementation strategy had a set of tradeoffs, particularly around the implementation complexity of tracking
whether a property has been initialized.
One thing that we noted from the presentation is that any method of introducing required properties is going to mean that types with
these contracts on their empty constructors will not be able to be used in generic scenarios `where T : new()`, as the empty constructor
is required to set some amount of properties. The presentation also did not cover how to do additive or subtractive contracts: ie, a
future factory method `MakeAMads` might want to specify "Everything from the normal `Person` constructor except `FirstName`", whereas
a copy constructor would want to specify "You don't have to initialize anything". Some work has started on designing this scenario,
but it needs some more work before it's in a state to get meaningful feedback.
A read of the room found that most of the LDM was leaning towards compile-time only validation of required properties. It does mean
that upgrading binary versions without recompiling can leave things in an invalid state, but the implementation strategies for
getting actual runtime breaks are very complex and we're not sure they're worth the tradeoff. The next step in design will be to
come back with a more concrete syntax proposal for how required properties will be stated, and how constructors will declare their
contracts.
### Triage
We only had time left to triage 1 issue:
#### Proposal: Permit trailing commas in method signatures and invocations
https://github.com/dotnet/csharplang/issues/391
This is a longstanding friction point in the language that has mostly positive sentiment on csharplang. We're also fairly confident
that it wouldn't break any possible language features other than omitted arguments, which the LDM is not particularly interested
in pursuing. That being said, the general gut reaction of most LDM members is something along the lines of "This looks wrong." We
certainly acknowledge that, despite some syntactic misgivings, this can be a very useful feature, particularly for source generators
and refactorings, as well as for just simply reordering parameters in a method call or definition. There are a couple of open issues
we'd need to resolve as well:
* What would happen with param arrays?
* Would tuple declarations be allowed to have trailing commas? This proposal actually seems like a natural way to allow oneples into
the language, in a similar style to Python, where the oneple must have a trailing comma.
##### Conclusion
Triage in X.0. We think this feature has a place in C#, but we don't think it will make the bar for what we're interested in with C# 10.

View file

@ -1,37 +0,0 @@
# C# Language Design Meeting for September 23rd, 2020
## Agenda
1. General improvements to the `struct` experience
## Quote of the day
- "This makes C# developers queasy. Refs can never be null! The runtime says that's not true, the C# type system is a lie."
## Discussion
Most of this session was dedicated to explaining the proposal itself, which can be found [here](https://github.com/dotnet/csharplang/blob/master/proposals/low-level-struct-improvements.md).
We covered the section `Provide ref fields` in full, but did not get beyond that in this session. What follows are some noted comments on the
proposal, as we did not have long discussions about the merits of the feature itself.
* The general driving principles of this proposal are to ease friction points in the language around structs. Today we have `Span<T>`, which is
a very useful type, but it uses a magic BCL-internal type called `ByReference<T>`. Span uses it to great effect, but because there are no compile
rules around its safety we can't expose it publicly. This leads to 3rd parties using reflection to get it, which just results in badness all
around. We'd like to allow the semantics to be fully specifiable in C# and enable it not just for the BCL, but for all types of structs. The LDM
generally agrees with this goal.
* There will need to be some metadata tool updates. In order for safe-to-escape analysis to work, we will have to ensure that all `ref` fields,
even private fields, appear in reference assemblies and similar locations to ensure that the compiler can correctly determine lifetimes.
* The rules around returning a ref to a ref parameter are not especially intuitive. We acknowledge that they are necessary given the lifetime
rules today with methods returning a `Span`, but we could introduce a `CapturesAttribute` or something similar to indicate that a `ref` parameter
is captured by the method, and thus allow passing it directly as a `ref` to `new Span<T>`.
* There is a workaround for this behavior: instead of taking a `ref T` in the constructor, take a `Span<T>`, which will ensure that all the
lifetimes line up. While this workaround is viable, we're somewhat worried it won't be straightforward enough of a solution.
* Allowing `ref` assignment after the fact could be done in a later version of C#. It's a good deal of work in the compiler (likely an entirely
new flow analysis pass) to correctly update the lifetimes, and we're not yet certain that the scenario is worth the effort. If this proves to be
a friction point for users, we can revisit.
* One scenario this spec does not consider is `ref` assignment in an object initializer, which is still part of the object construction phase.
This should be allowable, and we need to update the draft specification to address this case.
* `ref null` is going to be an annoying problem. Given that you can `default` structs in any of a number of ways, at some point it will be possible
to observe a `default` ref struct that has a `null` `ref`. While the runtime does have an `Unsafe.IsNullRef` helper method, it feels unnatural that
code that is entirely safe C# should have to use a method from the `Unsafe` class. Further, these newly-observable `null` `ref`s will will end up
everywhere, in much the same way that `null` ends up everywhere. We may need to think more about this problem.

View file

@ -1,207 +0,0 @@
# C# Language Design Meeting for September 28th, 2020
## Agenda
1. [Warning on `double.NaN`](#warning-on-double.nan)
2. [Triage](#triage)
1. [Proposal: more improvements to nullable reference types](#proposal-more-improvements-to-nullable-reference-types)
2. [Proposal: Required Properties](#proposal-required-properties)
3. [Proposal: Extend with expression to anonymous type](#proposal-extend-with-expression-to-anonymous-type)
4. [Proposal: Shebang (#!) Support](#proposal-shebang--support)
5. [Proposal: List Patterns](#proposal-list-patterns)
6. [Proposal: Add ability to declare global usings for namespaces, types and aliases by using a command line switch](#proposal-add-ability-to-declare-global-usings-for-namespaces-types-and-aliases-by-using-a-command-line-switch)
7. [Proposal: "Closed" enum types](#proposal-closed-enum-types)
8. [Top-level functions](#top-level-functions)
9. [Primary Constructors](#primary-constructors)
10. [Champion: Simplified parameter null validation code](#champion-simplified-parameter-null-validation-code)
11. [Proposal: Support generics and generic type parameters in aliases](#proposal-support-generics-and-generic-type-parameters-in-aliases)
12. [Support for method parameter names in nameof](#support-for-method-parameter-names-in-nameof)
## Quote of the day
- "On a rational basis I have nothing against this"
## Discussion
### Warning on `double.NaN`
https://github.com/dotnet/roslyn/issues/15936
We have an existing FxCop warning (CA2242) for invalid comparisons to `double.NaN`. We could consider bringing
that warning into the compiler itself as in a warning wave. However, as this analyzer is now shipped with the
.NET 5 SDK, is on by default, and deals more with API usage than with the language itself, it would also be fine
to leave it where it is.
#### Conclusion
Rejected. Leave the warning where it exists today.
### Triage
Today, we got through half the remaining issues in the C# 10.0 Candidate milestone
#### Proposal: more improvements to nullable reference types
https://github.com/dotnet/csharplang/issues/3868
We have a few open topics in improvements to nullable reference types for the C# 10.0 timeframe. We're currently
tracking LINQ improvements, Task-like type covariance, and uninitialized fields and constructors. This last point
will likely be handled in conjunction with the proposals for required properties and initialization debt. The
first two can be broken out to specific issues for the 10.0 timeframe.
##### Conclusion
File separate issues for the first two bullets, and triage into the 10.0 working set.
#### Proposal: Required Properties
https://github.com/dotnet/csharplang/issues/3630
We've already started talking about this one.
##### Conclusion
Triage into the 10.0 working set.
#### Proposal: Extend with expression to anonymous type
https://github.com/dotnet/csharplang/issues/3530
This proposal extends anonymous types to allow `with` expressions to change them, which we like a lot. In a way,
this extension makes anonymous types practically just anonymous _record_ types, as they have the rest of the properties
of a record already: value equality, ToString, etc. It also fits in well with the idea of generally extending `with`
expressions to be more broadly applicable. Since anonymous types cannot be exposed as types in a public API, generating
new `init` members and the `with` clone method is a non-breaking change. New compilations can take advantage of the
features, and old compilations don't get affected by them.
As part of discussing this issue, we hit upon the idea of additionally allowing `new { }` to be target-typed. If a `new`
expression that did not have `()` is assigned to something that matches its shape (such as a record with the correct)
property names, we could just allow that new expression to be treated as the target-type constructor, rather than as
creating a new anonymous type. If the target-type is `object` or `dynamic`, it will still result in an anonymous object
being created, and there may be some tricks to figuring out generic inference, but we think it might be a path forward
towards making target-typed new more regular with the rest of the language (a complaint we have already heard). A future
proposal for that will be filed.
##### Conclusion
Triage into C# 10.0 working set for consideration with the rest of the `with` extensions.
#### Proposal: Shebang (#!) Support
https://github.com/dotnet/csharplang/issues/3507
While could eventually be an interesting proposal, the tooling is not there currently, and we feel the discussion around
developer ergonomics in .NET 6 will shape our discussions in this area.
##### Conclusion
Triaged into X.0, and if the .NET tooling looks to add `dotnet run csfile`, we can consider again at that point.
#### Proposal: List Patterns
https://github.com/dotnet/csharplang/issues/3435
We like continuing to enhance the pattern support in C#, and are in general positive about this proposal. However, the
somewhat-fractured nature of .NET here works to our detriment, not just in the `Count` vs `Length` property names, but
in the general collection type sense. It would be nice to support `IEnumerable`, for example, which does not meet the
definitions set out in the proposal. Another consideration would be dictionary types: we don't have support for a
dictionary initializers specifically today, so having a decomposition step without a composition step would be odd, but
they would be a useful pattern nontheless. We also would like to see if we can find a way to make the syntax use braces
rather than square brackets to mirror collection initializers, though that will be difficult due to the empty property
pattern.
##### Conclusion
We'll spend design time on this in the C# 10 timeframe, though it may not make the 10.0 release itself. Triaged into the
10.0 working set.
#### Proposal: Add ability to declare global usings for namespaces, types and aliases by using a command line switch
https://github.com/dotnet/csharplang/issues/3428
This is, in many ways, a language-defining issue. The proposal itself is small, but it reflects the LDT's thinking of
future C# directions as a whole. It is also very divisive, both in the LDT and in the greater community, with a small
majority (both in the LDT and in the csharplang community) in favor of the feature. It introduces a level of magic to
the language that has been somewhat resisted in the past. Additionally, there is concern that there is no one set of
"base usings" that should be automatically included in files: a console app might only want to include `System`, while
an ASP.NET app might want to include a bunch of namespaces for various routing properties, controllers, and the like.
##### Conclusion
We'll discuss this more in the 10.0 timeframe. There could be interactions around the theme of developer QoL in the .NET
6 timeframe.
#### Proposal: "Closed" enum types
https://github.com/dotnet/csharplang/issues/3179
This proposal is linked to discriminated unions, in that it's a another type of closed hierarchy that C# does not support
today.
##### Conclusion
Triage into 10.0 working set, to be discussed with DU's and closed type hierarchies in general.
#### Top-level functions
https://github.com/dotnet/csharplang/issues/3117
This is the follow-up work from C# 9 that we did not conclude in the initial push for top-level statements in C#,
namely in allowing globally-accessible functions in C# to float at the top level without a containing class. One
concern with generalizing this feature is that we have 20 years of BCL design that does not account for top-level
functions, in addition to other well-known and used libraries. For consistency, these libraries would likely not
use this new feature, which would relegate this feature to minimal usage. While many LDT members like the feature
in general, and would likely introduce it if we were redesigning C# from the ground up, we don't believe that the
feature belong in the C# we have today. We will continue to investigate whether top-level functions defined today
(which are local functions to the implicit `Main` method) should be callable from within the current file, in order
to have better compat with CSX. However, the overall feature is rejected.
##### Conclusion
Rejected.
#### Primary constructors
https://github.com/dotnet/csharplang/issues/2691
We have a proposal for this, and we are mostly in agreement that we should see this through.
##### Conclusion
Triage into 10.0 working set.
#### Champion: Simplified parameter null validation code
https://github.com/dotnet/csharplang/issues/2145
We have an implementation of this mostly ready. It was done in such as way as to be usable by the BCL, and will
potentially save 7K+ lines of code there. Let's get it in.
##### Conclusion
Triage into 10.0 working set.
#### Proposal: Support generics and generic type parameters in aliases
https://github.com/dotnet/csharplang/issues/1239
We like the idea of improving aliases in general, and this could come into play when we talk about the previously-mentioned
global usings. There are multiple possible flavors here though: there's simple aliases like we have today, that are
freely convertible back to the underlying type, and then there are true opaque aliases that are not freely convertible
back to the underlying type. Ideally, these latter aliases would be zero cost, which will likely require some work in
conjunction with the runtime. Additionally, while we like the idea of making improvements here, we have a lot on the C# 10
plate currently, and think it would fit in better with the type enhancements we hope to make after 10.
##### Conclusion
Triaged into X.0.
#### Support for method parameter names in nameof
https://github.com/dotnet/csharplang/issues/373
We like this, and have the start of an implementation.
##### Conclusion
Triaged into 10.0 working set.

View file

@ -1,134 +0,0 @@
# C# Language Design Meeting for September 30th, 2020
## Agenda
1. `record structs`
1. [`struct` equality](#struct-equality)
2. [`with` expressions](#with-expressions)
3. [Primary constructors and `data` properties](#primary-constructors-and-data-properties)
## Quote of the Day
- "... our plan of record"
- "haha badum-tish"
## Discussion
Our conversation today focused on bringing records features to structs, and specifically what parts should apply
to _all_ structs, what should apply to theoretical `record` structs (if that's even a concept we should have), and
what should not apply to structs at all, regardless of whether it's a `record` or not. The table we looked at is
below, followed by detailed summaries of our conversations on each area. We did not come to a confirmed conclusion
on primary constructors/data properties this meeting, but we do have a general room lean, which has been recorded.
|Feature |All structs |Record structs|No structs|
|----------------------------------|--------------|--------------|----------|
|Equality |--------------|--------------|----------|
|- basic value equality | X (existing) | | |
|- == and strongly-typed `Equals()`| | X | |
|- `IEquatable<T>` | | X | |
|- Customized value equality | X | | |
|`with` |--------------|--------------|----------|
|- General support | X | | |
|- Customized copy | |explicit error| X |
|- `with`ability abstraction | | ? | |
|Primary constructors |--------------|--------------|----------|
|- Mutability by default | | | leaning |
|- Public properties | | leaning | |
|- Deconstruction | | leaning | |
|Data properties |--------------|--------------|----------|
|- Mutability by default | | | leaning |
|- Public properties | | leaning | |
### `struct` equality
`record`s in C# 9 are very `struct` inspired in their value equality: all fields in `record` a and b are compared
for equality, and if they are all equal, then a and b are also equal. This is the default behavior for all `struct`
types in C# today, if an `Equals` implementation is not provided. However, this default implementation is somewhat
slow, as it uses reflection. We've talked in the past about potentially generating an `Equals` implementation for
all struct types that would have better performance. However, we are definitely very concerned about potential size
bloat for doing this, particularly around interop types. Given those concerns, we don't think we can generate such
methods for all struct types. We then considered whether hypothetical `record` structs should get this implementation.
However, generating a good implementation of equality for structs almost seems like it's not a C# language issue at
all. More than just C# runs on the CLR, and it would be a shame if there was incentive to use a particular language
because it generates a better equality method. Further, since we can't do this for all `struct` types, it means we
would inevitably have to educate users that "`record struct`s generate better code for equality, so you may just
want to make your `struct` a `record` for that reason alone", which isn't great either. Given that, we'd rather work
with the runtime team to make the automatic `Equals` method better, which will benefit not just all C# structs,
but all `struct` types from all languages that run on the CLR.
Next, we looked at whether we should expose new equality operators and strongly-typed `Equals` methods on `struct`
types, as well as implementing `IEquatable<T>`. We again came to the conclusion that, for all existing `struct`
types, it would be too costly in metadata (and a potential breaking change for exposing a strongly-typed `Equals`
method or `IEquatable<T>`) to do this for all types. However, we do think that a gesture for opting into this
generation would be useful. Given that, we considered whether it was useful to have these be more granular gestures,
ie if a type could just opt-in to generating `IEquatable<T>` without the equality features. For these scenarios, we
feel that the need just isn't there, and that it should be an all-or-nothing opt-in.
Finally on this topic, we considered customized `Equals` implementations. This is a fairly simple topic: all structs
support customizing their definition of `Equals` today, and will continue to do so in the future.
#### Conclusion
All structs will continue to use the runtime-generated `Equals` method if none is provided. Making a `struct` a
`record` will be a way to opt-in to new surface area that uses this functionality. We will work with the runtime
team to hopefully improve the implementation of the generated `Equals` methods in future versions of .NET.
### `with` expressions
We considered whether all structs should be copyable via a `with` expression, or just some subset of them. On the
surface, this seems a simple question: all structs are copyable today, and we even have a dedicated CIL instruction
for this: `dup`. It seems trivial to enable this for any location where we know the type is a `struct` type, and
just emit the `dup` instruction. Where this becomes a more interesting question, though, is in the intersection
between all structs and any potential for customization of the copy behavior. We have plans to enable `with`
as a general pattern that any class can implement through some mechanism, and if structs can customize that behavior
it means that a struct substituted for a generic type `where T : struct` will behave incorrectly if that behavior
was customized. Additionally, if we extend `with`ability as a pattern and allow it to be expressed via some kind of
interface method, would structs be able to implement that method? Or would it get an automatic implementation of
that method?
An important note for structs is that, no matter what we do here with respect to `with`, structs are fundamentally
different than classes as they're _already_ copied all the time. Unless someone is ensuring that they always pass
a struct around via `ref`, the compiler is going to be emitting `dup`s all the time. While we could design a new
runtime intrinsic to call either `dup` or the struct's clone method if it exists, struct cloning behavior has long-
established semantics that we think users will continue to expect.
#### Conclusion
All `struct` types should be implicitly `with`able. No `struct` types should be able to customize their `with`
behavior. Depending on how we implement general `with` abstractions, `record` structs might be able to opt-in to them,
but will still be unable to customize the behavior of that abstraction.
### Primary constructors and `data` properties
Finally today, we considered the interactions of primary constructors, data properties, and structs. There are two
general ideas here:
1. `struct` primary constructors should mean the same thing as `class` primary constructors (with whatever behavior
we define later in this design cycle), and `record struct` primary constructors should mean the same thing as
`record` primary constructors (public init-only properties), or
2. `record struct` primary constructors should mean public, mutable fields.
Option 1 would provide a symmetry between record structs and record class types, while option 2 would provide a
symmetry between record structs and tuple types. In a sense, a record struct would just become a strongly-named tuple
type, and have all the same behaviors as a standard tuple type. You could then opt a record struct into being
immutable by declaring the whole type `readonly`, or declaring the individual parameters `readonly`. For example:
```cs
// Public, mutable fields named A and B
record struct R1(int A, int B);
// Public, readonly fields named A and B
readonly record struct R2(int A, int B);
```
A key point in the mutability question for structs is that mutability in a struct type is nowhere near as bad as
mutability in a reference type. It can't be mutated when it's the key of a dictionary, for example, and unless
refs to the struct are being passed around the user is always in control of the struct. Further, if a ref is passed
and it is saved elsewhere, that's a copy, and mutation to that copy doesn't affect the original. As always, we also
have easy syntax to make something readonly in C#, while not having an easy syntax for making it mutable. On the other
hand, the shortest syntax in a class record type is to create an immutable property, and it might be confusing if
we had differing behaviors between record classes and record structs.
We did not come to a conclusion on this topic today. A general read of the room has a _slight_ lean towards keeping
the behavior consistent with record classes, but a number of members are undecided as there are good arguments in
both directions. We will revisit this topic in a future meeting after having some time to mull over the options here.

View file

@ -1,71 +0,0 @@
# C# Language Design Meeting for October 5th, 2020
## Agenda
1. [`record struct` primary constructor defaults](#record-struct-primary-constructor-defaults)
2. [Changing the member type of a primary constructor parameter](#changing-the-member-type-of-a-primary-constructor-parameter)
3. [`data` members](#data-members)
## Quote of the Day
- "The problem with people who voted for immutable by default is that they can't change their opinion. #bad_dad_jokes"
## Discussion
### `record struct` primary constructor defaults
We picked up today where we left off [last time](LDM-2020-09-30.md#primary-constructors-and-data-properties), looking at what
primary constructors should generate in `record struct`s. We have 2 general axes to debate: whether we should generate mutable
or immutable members, and whether those members should be properties or fields. All 4 combinations of these options are valid
places that we could land, with various pros and cons, so we started by examing the mutable vs immutable axis. In C# 9, `record`
primary constructors mean that the properties are generated as immutable, and consistency is a strong argument for preferring
immutable in structs. However, we also have another analogous feature in C#: tuples. We decided on mutability there because it's
more convenient, and struct mutability does not carry the same level of concern as class mutability does. A struct as a
dictionary key does not risk getting lost in the dictionary unless it itself references mutable class state, which is just as
much of a concern for class types as it is for struct types. Even if we had `with` expressions at the time of tuples, it's
likely that we still would have had the fields be mutable. A number of C# 7 features centered around reducing unnecessary struct
copying, such as `readonly` members and ref struct improvements, and reducing copies in large structs by `with` is still a
useful goal. Finally, we have a better story for making a `struct` fully-`readonly` with 1 keyword, while we don't have a similar
story for making a `struct` fully-mutable with a similar gesture.
Next, we examined the question of properties vs fields. We again looked to our previous art in tuples. `ValueTuple` can be viewed
as an anonymous struct record type: it has value equality and is used as a pure data holder. However, `ValueTuple` is a type
defined in the framework, and its implementation details are public concern. As a framework-defined pure data holder, it has no
extra behavior to encapsulate. A `record struct`, on the other hand, is not a public framework type. Much like any other user-
defined `class` or `struct`, the implementation details are not public concern, but the concern of the creator. We have real
examples in the framework (such as around the mathematics types) where exposing fields instead of properties was later regretted
because it limits the future flexibility of the type, and we feel the same level of concern applies here.
#### Conclusion
Primary constructors in `record struct`s will generate mutable properties by default. Like with `record` `class`es, users will
be able to provide a definition for the property if they do not like the defaults.
### Changing the member type of a primary constructor parameter
In C# 9, we allow `record` types to redefine the property generated by a primary constructor parameter, changing the accessibility
or the accessors. However, we did not allow them to change whether the member is a field or property. This is an oversight, and
we should allow changing whether the member is a field or property in C# 10. This will allow overriding of the default decision
in the first section, giving an ability for a "grow-up" story for tuples into named `record struct`s with mutable fields if the
user wishes.
### `data` members
Finally today, we took another look at `data` members, and what behavior they should have in `record struct`s as opposed to
`record` `classes`. We had previously decided that `data` members should generate `public` `init` properties in `record` types;
therefore, the crucial thing to decide is if `data` should mean the same thing as `record` would in that type, or if the `data`
keyword should be separated from `record` entirely. In C# today, we have very few keywords that change the code they generate
based on containing type context, and making `data` be dependent on whether the member is in a `struct` or `class` could end up
being quite confusing. On the other hand, if `data` is "the short way to create a nominal record type", then having different
behavior between positional parameters and `data` members in a `struct` could also be confusing.
#### Conclusion
We did not reach a decision on this today. There are 3 proposals on the table:
1. A `data` member is `public string FirstName { get; set; }` in `struct` types, and `public string FirstName { get; init; }` in
`class` types.
2. A `data` member is `public string FirstName { get; init; }` in all types.
3. We cut `data` entirely.
We'll come back to this in a future LDM.

View file

@ -1,103 +0,0 @@
# C# Language Design Meeting for October 7th, 2020
## Agenda
1. [`record struct` syntax](#record-struct-syntax)
2. [`data` members redux](#data-members-redux)
3. [`ReadOnlySpan<char>` patterns](#readonlyspanchar-patterns)
## Quote of the Day
- "And we're almost there (famous last words, I'll knock on something)"
- "What about `record delegate`... does `record interface` make sense... I'll go away now"
## Discussion
### `record struct` syntax
First, we looked at what syntax we would use to specify `struct` types that are records. There are two possibilities:
```cs
record struct Person;
struct record Person;
```
In the former, `record` is the modifier on a `struct` type. In the latter, `struct` is the modifier on `record` types.
We also considered whether to allow `record class`.
#### Conclusion
By unanimous agreement, `record struct` is the preferred syntax, and `record class` is allowed. `record class` is
synonymous with just `record`.
### `data` members redux
With `data`, we come back to the same question we had at the end of [Monday](LDM-2020-10-05.md#data-members): should we have
`data` members, and if we do, what should they mean. `data` can be viewed as a strategy to try and get nominal records in a
single line, much like positional records. This is not a goal of brevity just for brevity's sake: in discriminated unions,
listing many variants as nominal records is a design goal, and single-line declarations are particularly useful for this case.
Many languages with discriminated unions based on inheritance introduce short class-declaration syntax for use in union
declarations, and that was a defining goal for where `record` types would be useful.
Another area worth examining is the original proposal for the `data` keyword. In the original proposal, primary constructors
would have meant the same thing in `record` types as well non-`record` types, and `data` would have been used as a modifier
on the primary constructor parameter to make it a public get/init property. `data` applied to a member, then, would have been
a natural extension and reuse of that keyword. With the original scenario gone, there are a few concrete scenarios we think
are highly related to, and will influence, the `data` keyword:
1. Use as a single-line in a discriminated union. While this is motivating, it's worth considering that, at least to some LDT
members, anything more than 2 properties as `data` members doesn't look great, and would perhaps work better as a multi-line
construct, which will line up visually. This seems a reasonable concern, so `data` may not be the solution we're looking for.
2. Use in required properties, as it seems likely that we will need some keyword to indicate that a property is a required
member in a type. While `data` could be orthogonal to some other required property keyword, it's likely there will be at least
some interaction as we'd likely want `data` to additionally imply required. It's also possible that we could have just a single
keyword to mark a property required, and it gives you what we believe are the useful defaults for such a property.
3. Use in general primary constructors as a way to say "give me the same thing a `record` would have for this parameter". This
would be somewhat resurrecting the original use case for the keyword, as we could then retcon `record` primary constructors
as implying `data` on all parameters for you, but you can then opt-in to them in regular primary constructors as well. `data`
members in a class, then, would again become a natural reuse and extension of the keyword on a primary constructor parameter.
#### Conclusion
We'll leave `data` out of the language for now. Our remaining motivating scenarios are things that will be worked on more in
C# 10 cycle, and absent clearer designs in those spaces the need and design factors for `data` are too abstract. Once we work
more on these 3 scenarios, we'll revisit `data` and see if a need for it has emerged.
### `ReadOnlySpan<char>` patterns
https://github.com/dotnet/csharplang/issues/1881
We have a community PR to implement the Any Time feature of allowing constant strings to be used to pattern match against
a `ReadOnlySpan<char>`. This would be acknowledging special behavior for `ReadOnlySpan<char>` with respect to constant strings,
but we already have acknowledged special behavior for `Span` and `ReadOnlySpan` in the language, around `foreach`. We also
considered whether this could be a breaking change, but we determined it could not be one: `ReadOnlySpan` cannot be converted
to `object` or to an interface as it is a ref struct, so if there exists a pattern today operating on one today the input type
of that pattern match must be `ReadOnlySpan<char>`. There were two open questions:
#### Should we allow `Span<char>`
The question is if `Span<char>` should also be allowed as well as `ReadOnlySpan<char>`. All of the same arguments about
compat apply to `Span<char>`. We also considered whether `Memory`/`ReadOnlyMemory` should be allowed inputs. Unlike `Span`/`ReadOnlySpan`,
though, there are backcompat concerns with `Memory` that cannot be overlooked, as they are not ref structs. It is very
easy to obtain a `Span`/`ReadOnlySpan` from a `Memory`/`ReadOnlyMemory`, however, so the need isn't as great.
##### Conclusion
`Span<char>` is allowed. `Memory<char>`/`ReadOnlyMemory<char>` are not.
#### Is this specific to `switch`, or can it be any pattern context
The original proposal here just mentioned `switch`. However, the implementation allows it to be used in any pattern context,
such as `is`.
##### Conclusion
Allowed in all pattern contexts.
#### Final Notes
We also discussed making sure that switching on a `Span`/`ReadOnlySpan` is as efficient for large switch statements as switching
on a string is. Over 6 cases, the compiler will take the hashcode of the string and use it to implement a jump table to reduce the
number of string comparisons necessary. This method is added to the `PrivateImplementationDetails` class in an assembly, and we
should make sure to do the same thing for `Span<char>` and `ReadOnlySpan<char>` here as well so the cost to using one isn't higher
than allocations would be from just doing a substring and matching with that.

View file

@ -1,57 +0,0 @@
# C# Language Design Meeting for October 12th, 2020
## Agenda
1. General improvements to the `struct` experience (continued)
## Quote of the Day
- "Only 5 people are going to use it..."
- "We said that about function pointers too."
## C# Community Ambassadors
We have been taking a look at https://github.com/dotnet/csharplang/discussions/3878, which is around how to help community
proposals into better shape to be looked at by LDM. We largely agree with the points raised, which is that most community
issues are in a state that isn't quite good enough to be sponsored by an LDT member, but also not controversial or generally
discouraged enough to be outright rejected. Our move to enabling discussions on the repo itself was the first step in this
direction: discussions are more free-form, allow multiple branching conversations, and we view them as having less requirements
towards creating one. The next step we're taking today is nominating a few community members to become ambassadors to the
community: @jnm2, @YairHalberstadt, and @svick. This role will be focussed around helping triage incoming issues and
discussions, helping community members get their proposals into a state that can realistically be looked at by LDT members
and potentially championed, and helping with deduplication as it is noticed. We're starting very small with this experiment:
if it proves successful, we can consider expanding the list to more members of the csharplang community, of which there are
several deserving candidates. As part of this, we're tentatively hoping to review promising community proposals at a more
regular cadence, hopefully monthly.
Community ambassadors are not members of the LDT and do not have the ability to champion issues. They will help us look at
deserving community proposals, and we value their input, as we value the input of the general community here.
## Discussion
Today, we finished going through the fixed fields proposal, found [here](https://github.com/dotnet/csharplang/blob/master/proposals/low-level-struct-improvements.md).
[Previously](LDM-2020-09-23.md), we made it through the `Provide ref fields` section of the specification. Today, we
finished going through the rest. Again, most the conversation was dedicated to the specification itself, but there were
a few points brought up that will be updated in the specification later:
* `ThisRefEscapes` is defined very narrowly in this proposal, not allowed on any virtual methods (including methods from
interfaces). In our initial investigations, we don't see a huge need for allowing it on interface methods. We can consider
this in the future if it ends up being a friction point, but will need a good amount of work around ensuring that OHI is
correctly respected.
* We considered the issue of whether we should use syntax for `ThisRefEscapes` and `DoesNotEscape`, and nearly-unanimously
decided on using attributes. Attributes allow us to have a more descriptive name that users are less likely to accidentally
use. Further, all this attribute is controlling is a `modreq`, not the actual implementation of the method. We have existing
attributes such as `SpecialNameAttribute` that control emit flags like this, so it's not unprecedented.
* The syntax for fixed buffer locals is actually quite generally attractive: it would be nice if we could remove the requirement
for specifying `fixed` in fields. It would further simplify the language: we even have parser code that parses this form today
so that we can give nicer errors to people coming from C/C++. It would further resolve an ambiguity: in the proposal today, old-
style fixed-size buffers are differentiated from new-style by whether or not the field is in an unsafe context: by omitting the
fixed, we have a completely different syntax that is unambiguous.
* We don't believe there is any real motivating scenario for either fixed multi-dimensional arrays or fixed jagged arrays of a
specific inner length. Jagged arrays of this form would work: `int[] array[10]`, where you have a fixed buffer of array references,
but allocating the inner array as part of the containing structure itself isn't currently seen as an important scenario.
Multidimensional arrays today need to call into CLR helper methods today and are generally slower. We can think about this later
if a scenario comes up.
* We might want to make "inline array" a first-class type in the CLR. This would allow for things such as substituting in type
parameters. This will largely be driven by the CLR design here.

View file

@ -1,138 +0,0 @@
# C# Language Design Meeting for October 14th, 2020
## Agenda
1. [Triage](#triage)
1. [Repeated Attributes in Partial Members](#repeated-attributes-in-partial-members)
2. [Permit a fixed field to be declared inside a readonly struct](#permit-a-fixed-field-to-be-declared-inside-a-readonly-struct)
3. [Do not require fixing a fixed field of a ref struct](#do-not-require-fixing-a-fixed-field-of-a-ref-struct)
4. [params Span<T>](#params-spant)
5. [Sequence Expressions](#sequence-expressions)
6. [utf8 string literals](#utf8-string-literals)
7. [pattern-based `with` expressions](#pattern-based-with-expressions)
8. [Property improvements](#property-improvements)
9. [File scoped namespaces](#file-scoped-namespaces)
10. [Discriminated Unions](#discriminated-unions)
11. [Efficient params and string formatting](#efficient-params-and-string-formatting)
12. [Allow omitting unused parameters](#allow-omitting-unused-parameters)
2. [Milestone Simplification](#milestone-simplification)
## Quote of the Day
- "I used to have a fidget cube, but then [redacted] took away my fidget cube because it was apparently a very annoying device for everyone else"
## Discussion
### Triage
#### Repeated Attributes in Partial Members
https://github.com/dotnet/csharplang/issues/3658
We discussed this briefly at the tail-end of C# 9 work, and came to the conclusion this is an issue for source generator authors and that we wanted
to make it work. Triaged into the working set.
#### Permit a fixed field to be declared inside a readonly struct
https://github.com/dotnet/csharplang/issues/1793
This is part of the feature we discussed Monday. Triage into the working set.
#### Do not require fixing a fixed field of a ref struct
https://github.com/dotnet/csharplang/issues/1792
We generally like the idea of this feature: language-wise it's small, makes sense, and is an annoyance for users of ref structs. However, the
implementation of `fixed` in Roslyn has historically been an issue with a long bug trail coming every time we need to make a change. We think
this makes sense the next time we need to a larger feature around fixed that would force us to refactor the handling of `fixed` in the compiler.
Until then, we don't think the implementation cost is worth it. Triaged to the backlog.
#### params Span<T>
https://github.com/dotnet/csharplang/issues/1757
We like this feature. We'll need to carefully design the overload resolution rules such that it wins out over existing params arrays. Libraries
can then intentionally opt-in by introducing `Span<T>` overloads, just like they can introduce any new overload today that causes a change in
behavior when recompiled. Triaged into the working set.
#### Sequence Expressions
https://github.com/dotnet/csharplang/issues/377
Related to #3038, #3037, and #3086, which are all in the working set. We'll be taking a look at the whole scenario in the upcoming design period,
so this is triaged into the working set with the others.
#### utf8 string literals
https://github.com/dotnet/csharplang/issues/184
We need the runtime to make progress here. While we could consider ways to make it easier to declare constant utf-8 byte arrays, we feel that
would likely box us in when the runtime wants to move forward in this area. When they're ready, we can put this back on the agenda. Triaged
into the backlog.
#### pattern-based `with` expressions
https://github.com/dotnet/csharplang/issues/162
This is part of the next round of record work, so into the working set it goes. We may need to think about how general object might be able to
do object reuse as part of a `with` (Roslyn would not be able use it to replace `BoundNode.Update` as spec'd for records, for example), so that
is a scenario we need to keep in mind as we generalize.
#### Property improvements
https://github.com/dotnet/csharplang/issues/133
https://github.com/dotnet/csharplang/issues/140
We've discussed these recently in LDM. We're moving forward with design, starting with #133. Triaged into the working set.
#### File scoped namespaces
https://github.com/dotnet/csharplang/issues/137
Triaged into the working set. We'll need to come up with a more complete proposal than we have currently, particularly considering how it will
interact with top-level statements, but it doesn't seem too difficult.
#### Discriminated Unions
https://github.com/dotnet/csharplang/issues/113
This is one of the next big C# tent poles we're looking to address, and we're working on an updated proposal after having some time to ruminate
on the previous proposals in the area and post initial records. We have lots of design work to do, so into the working set it goes.
#### Efficient params and string formatting
https://github.com/dotnet/csharplang/issues/2302
Interpolated strings are a pit of failure in certain scenarios, such as logging, where formatting costs are incurred up front even if they're
not needed. We'll keep this in the working set to keep iterating on proposals. We know we want to do some work here, but we're not sure exactly
how it will function yet.
#### Allow omitting unused parameters
https://github.com/dotnet/csharplang/issues/2180
We had some initial questions around the metadata representation of discards: should the be nameless, for example? We generally like proposals
that put discards in more places, and we're willing to look at a complete proposal if one is presented. Triaged to any time.
### Milestone Simplification
Today, we have quite a few milestones that mean various things at various points in time, and it's hard for outsiders to keep track of what is
on track for what. This is further complicated by the fact that sometimes things are just not known: we could be working on a feature with every
intention to put it in the next version of C#, but fully acknowledging it might not make it. Or we may be doing design work for a feature that
we know _definitely_ will not make it into the next version of C#, but needs to have work done or we'll never get it at all. For this reason,
we're simplifying our milestones to be more clear about what the state of things is:
* Working Set is the set of proposals that the LDT is currently actively working on. Not everything in this milestone will make the next version
of C#, but it will get design time during the upcoming release.
* Backlog is the set of proposals that members of the LDT have championed, but are not actively working on. While discussion and ideas from the
community are welcomed on these proposals, the cost of the design work and implementation review on these features is too high for us to consider
community implementation until we are ready for it.
* Any Time is the set of proposals that members of the LDT have championed, but are not being actively worked on and are open to community
implementation. We'll go through these shortly and label the ones that need to have a specification added vs the ones that have an approved spec
and just need implementation work. Those that need a specification still need to be presented during LDM for approval of the spec, but we are
willing to take the time to do so at our earliest convenience.
* Likely Never is the set of proposals that the LDM has reject from the language. Without strong need or community feedback, these proposals will
not be considered in the future.
* Numbered milestones are the set of features that have been implemented for that particular language version. For closed milestones, these are
the set of things that shipped with that release. For open milestones, features can be potentially pulled later if we discover compatability or
other issues as we near release.

View file

@ -1,132 +0,0 @@
# C# Language Design Meeting for October 21st, 2020
## Agenda
1. [Primary Constructors](#primary-constructors)
2. [Direct Parameter Constructors](#direct-parameter-constructors)
## Quote of the Day
- "Hopefully Seattle doesn't wash into the ocean"
## Discussion
### Primary Constructors
https://github.com/dotnet/csharplang/discussions/4025
We started today by examining the latest proposal around primary constructors, and attempting to tease out the possible
behaviors of what a primary constructor could mean. Given this sample code:
```cs
public class C(int i, string s) : B(s)
{
...
}
```
there are a few possible behaviors for what those parameters mean:
1. Parameter references are only allowed in initialization contexts. This means you could assign them to a property, but
you couldn't use them in a method that runs after the class has been initialized.
2. Parameter references are allowed throughout the class, and if they're referenced from a non-initialization context then
they are captured in the type, but are not considered fields from a language perspective. This is the proposed behavior in
the linked discussion.
3. Parameter references are automatically captured to fields of the same name.
We additionally had a proposal in conjunction with behavior 1: You can opt in to having a member generated by adding an
accessibility to the parameter. So `public class C(private int i)` would generate a private field `i`, in addition to having
a constructor parameter. This is conceivably not tied to behavior 1 however, as it could also apply to behavior 2 as well.
It would additionally need some design work around what type of member is generated: would `public` generate a field or a
property? Would it be mutable or immutable by default?
To try and come up with a perferred behavior here, we started by taking a step back and examining the motivation behind
primary constructors. Our primary (pun kinda intended) motivation is that declaring a property and initializing it from
a constructor is a boring, repetitive, boilerplate-filled process. You have to repeat the type twice, and repeat the name
of the member 4 times. Various IDE tools can help with generating these constructors and assignments, but it's still a lot
of boilerplate code to read, which obscures the actually-interesting bits of the code (such as validation). However, it is
_not_ a goal of primary constructors to get to 1-line classes: we feel that this need is served by `record` types, and that
actual classes are going to have some behavior. Rather, we are simply trying to reduce the overhead of describing the simple
stuff to let the real behavior show through more strongly.
With that in mind, we examined some of the merits and disadvantages of each of these:
1. We like that parameters look like parameters, and adding an accessibility makes it no longer look like a parameter. There's
definitely a lot to debate on what that accessibility should actually do though. There are some concerns that having the
parameter not be visible is non-obvious to users: to solve this, we could make then visible throughout the type, but have it
be an error to reference in a location that is not an initialization context (and a codefix to add an accessibility to make
it very easy to fix). This allows users to be very explicit about the lifetime of variables.
2. This variation of the proposal might feel more natural to users, as the variable exists in an outer "scope" and is therefore
visible to all inner scopes. There is some concern, however, that silent captures could mean that the state of a class is no
longer visible: you'll have to examine all methods to determine if a constructor parameter is captured, which could be suboptimal.
3. This the least flexible of the proposals, and wasn't heavily discussed. It would need some amount of work to fit in with the
common autoprop case, where the others could work without much work (either via generation or by simple assignment in an
initializer for the autoprop).
In discussing this, we brought another potential design: we're considering primary constructors to eliminate constructor boilerplate.
What if we flipped the default, and instead generated a constructor based on the members, rather than generating potential members
based on a constructor. A potential strawman syntax would be something like this:
```cs
// generate constructor and assignments for A and B, because they are marked default
public class C
{
default public int A { get; }
default public string B { get; }
}
```
There are a bunch of immediate questions around this: how does ordering work? What if the user has a partial class? Does this
actually solve the common scenario? While we think the answer to this is no, it does bring up another proposal that we
considered in the C# 9 timeframe while considering records: Direct Parameter Constructors.
## Direct Parameter Constructors
https://github.com/dotnet/csharplang/issues/4024
This proposal would allow constructors to reference members defined in a class, and the constructor would then generate a matching
parameter and initialization for that member in the body of the constructor. This has some benefits, particularly for class types:
* Many class types have more than one constructor. It's not briefer than primary constructors declaring members for a class with
just one constructor, but it does get briefer as you start adding more constructors.
* We believe, at least from our initial reactions, that this form would be easier to understand than accessibility modifiers on the
parameters.
There are still some open questions though. You'd like to be able to use this feature in old code, but if we don't allow for customizing
the name of the parameter, then old code won't be able to adopt this for properties, as properties will almost certainly have different
casing than the parameters in languages with casing. This isn't something we can just special case for the first letter either: there
are many examples (even in Roslyn) of identifiers that have the first two letters capitalized in a property and have them both lowercase
in a parameter (such as `csharp` vs `CSharp`). We briefly entertained the idea of making parameter names match in a case-insensitive
manner, but quickly backed away from this as case matters in C#, working with casing in a culture-sensitive way is a particularly hard
challenge, and wouldn't solve all cases (for example, if a parameter name is shortened compared to the property).
We also examined how this feature might interact with the accessibility-on-parameter proposal in the previous section. While they are
not mutually exclusive, several members of the LDT were concerned that having both of these would add too much confusion, giving too
many ways to accomplish the same goal. A read of the room found that we were unanimously in favor of this proposal over the accessibility
proposal, and there were no proponents of adding both proposals to the language.
Finally, we started looking at how initialization would work with constructor chaining. Some example code:
```cs
public class Base {
public object Prop1 { get; set; }
public virtual object Prop2 { get; set; }
public Base(Prop1, Prop2) { Prop2 = 1; }
}
public class Derived : Base
{
public new string Prop1 { get; set; }
public override object Prop2 { get; set; }
public Derived(Prop1, Prop2) : base(Prop1, Prop2) { }
}
```
Given this, the question is whether the body of `Derived` should initialize `Prop1` or `Prop2`, or just one of them, or neither of them.
The simple proposal would be that passing the parameter to the base type always means the initialization is skipped, but that would
mean that the `Derived` constructor has no way to initialize the `Prop1` property, as it can no longer refer to the constructor parameter
in the body, and `Base` certainly couldn't have initialized it (since it is not visible there). There are a few questions like this that
we'll need to work out.
## Conclusions
Our conclusions today are that we should pursue #4024 in ernest, and come back to primary constructors with that in mind. Several members
are not convinced that we need primary constructors in any form, given that our goal is not around making regular `class` types have only
one line. Once we've ironed out the questions around member references as parameters, we can come back to primary constructors in general.

View file

@ -1,109 +0,0 @@
# C# Language Design Meeting for October 26st, 2020
## Agenda
1. [Pointer types in records](#pointer-types-in-records)
2. [Triage](#triage)
1. [readonly classes and records](#readonly-classes-and-records)
2. [Target typed anonymous type initializers](#target-typed-anonymous-type-initializers)
3. [Static local functions in base calls](#static-local-functions-in-base-calls)
## Quote of the Day
- "And I specialize in taking facetious questions and answering them literally"
## Discussion
### Pointer types in records
Today, you cannot use pointer types in records, because our lowering will use `EqualityComparer<T>.Default`, and pointer
types are not allowed as generic type arguments in general. We could specially recognize pointer types here, and use a
different equality when comparing fields of that type. We have a similar issue with anonymous types, where pointers are
not permitted for the same reason (and indeed, Roslyn's code for generating the equality implementation is shared between
these constructs). We would also need consider every place record types can be used if we enabled this: for example, what
would the experience be when attempting to pattern deconstruct on a record type, as pointer types are not allowed in patterns
today? It also might not be a good idea to introduce value equality based on pointer types to class types, as this is not
well-defined for all pointer types (function pointers, for example). Finally, the runtime has talked several times about
enabling pointer types as generic type parameters, and if they were to do so then the rules for this might fall out at that
time.
#### Conclusion
Triaged to the Backlog. We're not convinced this needs to be something that we enable right now, and may end up being resolved
by fallout from other changes.
### Triage
#### readonly classes and records
https://github.com/dotnet/csharplang/issues/3885
This proposal would allow marking a class type `readonly`, ensuring that all fields and properties on the type must be `readonly`
as well. Several familiar questions were immediately raised, namely around the benefit. `readonly` has a very specific benefit
for struct types, around allowing the compiler to avoid defensive copies where they would otherwise be necessary. For
`readonly` classes, there is no clear similar advantage. We might not even emit such information to metadata, and the main
benefit would be for type authors, not for type consumers. There is also some concern about whether this would be confusing to
users, particularly if this does not apply to an entire hierarchy. If you depend on a non-Object base type that has mutable,
then the benefits of using `readonly` are not as clear, even for a type author. Similarly, if a non-`readonly` type can inherit
from a `readonly` type, that means that any guarantees on the current type aren't very strong, as mutation can occur under the
hood anyway. `readonly` in C# today always means shallow immutability, so there is an argument to be made that this level of
hierarchy-mutability is not too different.
We also looked at the question of whether this feature should just be analyzer. There is certainly argument for that: particularly
if there is no hierarchy impact, it seems a perfect use case for an analyzer. However, this is a case where we allow the keyword
on one set of types, while not allowing it on a different set of types. Further, unlike many such proposals, we already have a
C# keyword that is perfect for the scenario.
##### Conclusion
Triaged into the Working Set. We'll look at this with low priority, and particularly try to see what the scenarios around
hierarchical enforcement look like, as those were more generally palatable to LDT members.
#### Target typed anonymous type initializers
https://github.com/dotnet/csharplang/issues/3957
This is a proposal to address some cognitive dissonance we have with object creation in C#: you can leave off the parens if you
have an object initializer, but only if you specify the type. While it does save 2 characters, that is not a primary motivation
of this proposal. There are grow-up stories for other areas we could explore in this space as well: we could allow F#-style
object expressions, for example, or borrow from Java and allow anonymous types to actually inherit from existing types/interfaces.
However, we have a number of concerns about the compat aspects of doing this, where adding a new `object` overload can silently
change consumer code to call a different overload and create an anonymous type. In these types of scenarios, it might even be
impossible to determine if the user made an error: if they typed a wrong letter in the property name, for example, we might be
forced to create an anonymous type silently, instead of erroring on the invalid object initializer.
We also briefly considered more radical changes to the syntax: for example, could we allow TS/JS-style object creation, with
just the brackets? However, this idea was not very well received by the LDM.
##### Conclusion
Triaged into the Backlog. While we're open to new proposals in this space that significantly shift the bar (such as around new
ways of creating anonymous types that inherit from existing types), we think that this proposal could end up conflicting with
any such future proposals and should be considered then.
#### Static local functions in base calls
https://github.com/dotnet/csharplang/issues/3980
This is a proposal that, depending on the exact specification, would either be a breaking change or have complicated lookup rules
designed to avoid the breaking change. It also requires some deep thought into how the exact scoping rules would work. Today,
locals introduced in the `base` call are visible throughout the constructor, so we would have to retcon the scoping rules to
work something like this:
1. Outermost scope, contains static local functions
2. Middle scope, contains the base clause and any variables declared there
3. Inner scope, contains the method body locals and regular local functions.
This also raises the question of whether we should stop here. For example, it might be nice if `const` locals could be used as
parameter default values, or if attributes could use names from inside a method body. We've had a few proposals for creating
various parts of a "method header" scope (such as https://github.com/dotnet/csharplang/issues/373), we could consider extending
that generally to allow this type of thing. Another question would be: why stop at `static` local functions? We could allow
regular local functions in the base clause, and leverage definite assignment to continue doing the same things it does today to
make sure that things aren't used before assignment. This might work well with a general "method header" scope, instead of the
scheme proposed above. Finally, we considered simply allowing the `base` call to be done in the body of the constructor instead,
a la Visual Basic. This has some support, and would allow us to avoid the question of a method header scope by simply allowing
users to move the base call to where the local function is visible.
##### Conclusion
Triaged into the Working Set. We like the idea, and have a few avenues to explore around method header scopes or allowing the
base call to be moved.

View file

@ -1,60 +0,0 @@
# C# Language Design Meeting for November 4th, 2020
## Agenda
1. [Nullable parameter defaults](#nullable-parameter-defaults)
2. [Argument state after call for AllowNull parameters](#argument-state-after-call-for-allownull-parameters)
## Quote of the Day
No particularly amusing quotes were said during this meeting, sorry.
## Discussion
### Nullable parameter defaults
https://github.com/dotnet/csharplang/pull/4101
We started today by examining some declaration cases around nullable that we special cased in our our initial implementation, but
felt that we should re-examine in light of `T?`. In particular, today we do not warn when you create a method signature that assigns
`default` to `T` as a default value. This means that it's possible for generic substitution to cause bad method signatures to be
created, where a `null` is assigned to a non-nullable reference type. The proposal, then, is to start warning about these cases, in
both C# 8 and 9. In C# 8, the workaround is to use `AllowNull` on that parameter, and C# 9 would allow `T?` for that parameter. There
was no pushback to this proposal.
As part of this, we also considered the other locations in this example. For example, we could issue a warning at the callsite of such
a method. The proposal would be to expand the warnings on nullable mismatches in parameters to implicit parameters as well. This could
end up causing double warning, if both the method and the callsite get a warning here, but it might be able to help users who are using
otherwise unannotated methods, or libraries compiled with an older version of the compiler that did not warn here. There is some
concern, though, that putting a warning at the callsite is the wrong location. It was the method author that created this invalid
signature, and we'd be punishing users with additional warnings. Presumably, if the author allows `null`, they're appropriately
handling it, even if the code is still oblivious or in an older version of C#.
#### Conclusion
The original proposal is approved. We'll continue looking at the callsite proposal as an orthogonal feature and come back when we have
a complete proposal to review.
### Argument state after call for AllowNull parameters
https://github.com/dotnet/csharplang/discussions/4102
https://github.com/dotnet/roslyn/issues/48605
Next, we looked at fallout over a previous decision to update the state of variables passed as parameters to a method. This allowed us
to bring the behavior of `void M([NotNull] string s)` and `void M(string s)` in line, which caused issues for the BCL (as it meant
that any change to add `NotNull` to a parameter would be a good change to make, and they were not interested in updating thousands of
methods to do this). However, it caused an unfortunate side effect: `void M([AllowNull] string s)` would have no warnings, and would
silently update the parameter state to not null, even though there was absolutely no way `M` could have affected the input argument as
it was not passed by ref. We considered 2 arguments for this:
1. Perhaps the method isn't annotated correctly? The real-world example here is `JsonConvert.WriteJson`, and there is an argument to be
made that in C# 9, this parameter would just be declared as `T?`, solving the issue. However, it does feel somewhat obvious that this
method shouldn't update the state of the parameter.
2. We loosen the "effective" resulting type of the parameter based on the precondition, if the parameter is by-value. `[AllowNull]` would
loosen the effective resulting type to `T?`, which would not make any changes to the current state of the argument. We might also do the
inverse for `DisallowNull`.
#### Conclusion
We ran out of time today, but are interested in approach 2 above. We'll come back with a complete proposal for a future LDM and examine it
again.

View file

@ -1,127 +0,0 @@
# C# Language Design Meeting for November 11th, 2020
## Agenda
1. [IsRecord in metadata](#isrecord-in-metadata)
2. [Triage](#triage)
1. [AsyncMethodBuilder](#asyncmethodbuilder)
2. [Variable declarations under disjunctive patterns](#variable-declarations-under-disjunctive-patterns)
3. [Direct constructor parameters](#direct-constructor-parameters)
4. [Always available extension methods](#always-available-extension-methods)
5. [Allow `nameof` to access instance members from static contexts](#allow-nameof-to-access-instance-members-from-static-contexts)
6. [Add `await` as a dotted postfix operator](#add-await-as-a-dotted-postfix-operator)
## Quote of the Day
- "Alright, I'm going to make an analogy to social security here."
## Discussion
### IsRecord in metadata
https://github.com/dotnet/csharplang/issues/4121
We discussed a few different ways to tackle this issue, which relates to customers depending on the presence of the `<Clone>$` method
as a way of determining if a type is a `record` or not. First, there are theoretically some ways we could retrofit this method to work
as an identifying characteristic, such as by marking `<Clone>$` methods on non-record types, instead of marking the record types in
some manner. However, this approach would have to square with `struct` records, which may or may not have that special method. We also need
to understand some of the dependent scenarios better: we understand the IDE scenario pretty well, we want to be able to have QuickInfo
and metadata-as-source reflect the way the type was declared. However, we don't have an understanding of the EF scenario, and what it
would want to do for, say, a non-record class that inherits from a record type. Finally, we considered time frames, and came to the
conclusion that the proposed solution would work fine if we wait until C# next to introduce it, and does not require being rushed out the
door to be retconned into C# 9: the proposed solution is backwards compatible, as long as it is introduced at the same time as
class/record cross inheritance.
#### Conclusion
Into the Working Set, we'll consider this issue in conjunction with class/record cross-inheritance.
### Triage
#### AsyncMethodBuilder
https://github.com/dotnet/csharplang/issues/1407
We generally like this proposal, as it solves a real need in the framework while creating a generalized feature that can be plugged into
more libraries. We did have a couple of questions come up:
1. Should we allow this on just the method, or also the type/module level? This seems to be similar to `SkipLocalsInit`, and could be
tedious to rep-specify everywhere.
2. Can this solve `ConfigureAwait`? We don't think so: this controls the method builder, not the meaning of `await`s inside the method,
so while it could potentially change whether a method call returns a task that synchronizes to the thread context by default, it could
only do that for methods defined in your assembly, which would just lead to confusing behavior.
##### Conclusion
Triaged into the Working Set, we'll work through the proposal in a full LDM session soon.
#### Variable declarations under disjunctive patterns
https://github.com/dotnet/csharplang/issues/4018
We like this proposal. There are a couple of open issues/questions that need to be addressed:
1. We need a rule that says when you are allowed to redeclare existing variables. It needs to cover multiple switch case labels, while
also not permitting things declared outside the switch label to be redeclared.
2. How identical do the types need to be? Are nullability differences permitted? ie, are `(object?, object)` and `(object, object?)` the
same for the purposes of this feature? It seems like they may have to be.
##### Conclusion
Triaged into the Working Set. We'll take some time and consider these questions, and we should also consider alternatives at the same time,
such as an `into` pattern that would allow a previously-declared variable to be assigned in a pattern, including ones declared outside a
pattern.
#### Direct constructor parameters
https://github.com/dotnet/csharplang/issues/4024
We discussed this feature during our last look at primary constructors, and our conclusion is that we need to explore the space more fully
with both features in mind. There are concerns about abstraction leaks, particularly with property casing.
##### Conclusion
Triaged into the Working Set, to be considered in conjunction with primary constructors.
#### Always available extension methods
https://github.com/dotnet/csharplang/issues/4029
There was some strong negative reaction to this proposal. However, presented another way it's more interesting: users who use `var` need
to include `using`s they otherwise do not need in order to access these types of extension methods, whereas users who do not use `var`
will already have the relevant `using` in scope, and will thus see these extension methods. These types of methods are also often ways of
working around various C# limitations, such as lack of specialization, and would naturally be defined on the type itself if it was possible.
We are concerned with doing anything in this space with extension everything/roles/type classes on the horizon, as we don't want to change
extension methods in a way that we'd regret with those features.
##### Conclusion
Triaged into the backlog. We'll consider this in conjunction with extension everything.
#### Allow `nameof` to access instance members from static contexts
https://github.com/dotnet/csharplang/issues/4037
There is some feeling that this is basically just a bug in the spec (or is just an area where it's not super clear, and it's a bug in the
implementation). We do think this is generally good: yes, the scenario could just use `string.Length`, but that is not really what the user
intended. They wanted the `Length` property on `P`, and if `P` changes to a different type that no longer has `Length`, there should be an
error there. Without this, the cliff that `nameof` tries to solve is just moved further, not removed.
##### Conclusion
Triaged into Any Time. We'd accept a community contribution here: it needs to only permit exactly this scenario, not allow any new types of
expressions in `nameof`.
#### Add `await` as a dotted postfix operator
https://github.com/dotnet/csharplang/issues/4076
The LDT has very mixed reactions on this. While we are sympathetic to the desire to make awaits more chainable, and the `.` can be viewed
as the pipeline operator of the OO world, we don't think this solves enough to make it worth it. Chainability of `await` expressions
isn't the largest issue on our minds with `async` code today: that honor goes to `ConfigureAwait`, which this does not solve. We could go
a step further with this form by making it a general function that would allow `true`/`false` parameters to control the thread context
behavior, but given our mixed reaction to the syntax form as a whole we're not optimistic about the approach. A more general approach that
simplified chaining generally for prefix operators would be more interesting.
##### Conclusion
Rejected. We do like the space of improving `await`, but we don't think this is the way.

View file

@ -1,81 +0,0 @@
# C# Language Design Meeting for November 16th, 2020
## Agenda
1. [Ternary comparison operator](#ternary-comparison-operator)
2. [Nominal and collection deconstruction](#nominal-and-collection-deconstruction)
3. [IgnoreAccessChecksToAttribute](#ignoreaccesscheckstoattribute)
## Quote of the Day
- "If it turns out to be really hard, give it to a smarter compiler dev"
- "The name is not good: It should be the BreakTermsOfServiceAttribute"
## Discussion
### Ternary comparison operator
https://github.com/dotnet/csharplang/issues/4108
This proposal centers around add a bit of syntax sugar to simply binary comparisons, where a user might want to compare 3 objects
for ascending or descending order. Today, the user would have to write `a < b && b < c`, but with this proposal they would just
write `a < b < c`. In order to deal with the potential ambiguities, we'd have to first attempt to bind these scenarios as we would
today, and if that fails then attempt to bind them as this new "relational chaining" form. This feature would need to have a very
specific pattern: if we were to allow `a < b > c`, for example, that could be syntactically ambiguous with a generic, and would need
to keep binding to that as it would today. We therefore are only interested in strictly-ordered comparisons: all comparisons in a
chain should be less-than/less-than-or-equal, or greater-than/greater-than-or-equal, without mixing between the 2 orders. We are
also worried about the compile-time cost of double-binding here, particularly since the most-likely binding will have to be done second,
in order to preserve backwards compatability. We also considered allowing more than 3 objects in such a chain: we like the idea, but
it will require some spec work as it does not just fall out of the current specification.
#### Conclusion
Triaged into Any Time. This needs some specification work to allow the 4 or more operators, which would likely be similar in form to
the null-conditional operator. Additionally, any implementation will have to take steps to address potential perf issues and demonstrate
that it does not adversely affect compilation perf on real codebases.
### Nominal and collection deconstruction
https://github.com/dotnet/csharplang/issues/4082
This feature provides unity between patterns and deconstruction assignment. Today, we have tuple deconstruction assignment, and tuple
patterns. They evolved in the opposite direction: we started with tuple deconstruction assignment, then added general patterns to the
language. We now consider adding nominal deconstruction assignment, to complete the symmetry between the feature sets.
One thing we want to be careful of here is to not go to far down the path of replicating patterns in assignment. A pattern in an `is`
or `switch` forces the user to deal with the case that the pattern did not match, which is not present here. For nominal deconstruction,
we can leverage nullable reference types: the user will get a warning if they attempt to deconstruct an element that could be null. For
list patterns, though, there is no similar level of warning, and we want to be careful of creating a pit of failure that will result in
exceptions at runtime. We are also concerned about some of the aspects of allowing names to be given to outer structures, such as allowing
`var { Range: { Column: column } range } = GetRange();`. This could mix badly with allowing existing variable reuse: in patterns today,
the `{ ... } identifier` syntax always introduces a new variable, which we think would end up being confusing. We very wary of allowing
patterns to match into existing variables because it would introduce side-effects to patterns, which is very concerning. Finally, given
that we haven't yet designed regular list patterns, we think we should hold off on list deconstruction assignment until those are complete,
at which point we should have a discussion around whether we should have them at all.
#### Conclusion
Nominal deconstruction assignment is accepted into the working set. Let's split list deconstruction assignment into a separate issue, which
will be followed up on after list patterns are designed. Open questions exist on whether we should allow names on patterns themselves.
### IgnoreAccessChecksToAttribute
We had a very spirited discussion around this attribute, which is essentially the inverse of `InternalsVisibleToAttribute`. Where IVT
allows an author to grant access to a specific other dll, this allows a specific other dll to grant themselves access to an author. There
are many challenges around this scheme that fundamentally affect the entire ecosystem, and those discussions need to happen at a .NET
ecosystem level, rather than at a language level, even though most of the implementation work will fall on the compiler. Ref assemblies,
for example, do not have internal members today. There also needs to be discussions on how we would enforce the "use at your own risk"
aspect of this feature. We can say that all we want, but at the end of the day if VS were to take a dependency on an internal Roslyn
API that we need to change, it could block shipping until either Roslyn readded the API or the dependency was removed. Given our
experiences with `InternalsVisibleToAttribute` already, we're not certain that this burden of risk will be correctly shouldered by the
ones actually taking on the risk.
#### Conclusion
Tabled for now. Discussion needs to happen at a higher level.
## Working Set Themes
With our discussions today, we have finished working through our current triage backlog! We've collected the various issues and themes
in our working set and created a meta-issue to track them all: https://github.com/dotnet/csharplang/issues/4144. We've locked the issue
to ensure that it stays a clean space. For discussion on a particular topic, please see the topic issue, or create a new discussion.

View file

@ -1,75 +0,0 @@
# C# Language Design Meeting for December 2nd, 2020
## Agenda
1. [Partner scenarios in roles and extensions](#partner-scenarios-in-roles-and-extensions)
## Quote(s) of the Day
- "Have you noticed how similar what you just said is to function pointers?"
- "This is a modern version of COM aggregation." "But in a good way."
## Discussion
### Partner scenarios in roles and extensions
Today, we heard from partner teams on the Azure SDK and on ASP.NET, talking about friction they currently encounter
with some scenarios that might be addressable through roles, extension everything, or potentially some other solution.
#### ASP.NET Feature Collections
ASP.NET models contexts through an aggregation system that allows different services to be composed onto a single `HttpContext`.
For example, adding TLS to a session involves creating a TLS connection, wrapping the existing connection, and adding it
as an available feature to the context. This underlying connection could be one of several types connections: it could be
routed through a socket, or it could be in-process, or any of a number of other connection mechanisms, each with its own set
of properties. It is possible to retrieve each of these sets of features from a `Get` method on the context, but this is
cumbersome and not generally extensible: for their users, it would be nice to be able to expose a view over a context or
connection that exposed the underlying properties.
This scenario seems like a clear-cut use case for roles and extension-everything as we last discussed them. A role could be
used to expose a grouping of properties on an upper layer from a lower layer. In fact, the ASP.NET architecture was designed
with the eventual intention of using extension properties to remove a number of extension methods that they have in place
today to expose these underlying properties from a decorated type. Of the 3 scenarios we discussed today, this seems the
most obviously-addressed by the existing proposal.
#### Azure SDKs
The Azure SDK scenario presents a more interesting challenge. Feature collections were designed with C# in mind, meaning
that both types and type names were thoughtfully designed when creating the API. The Azure SDK (and by extension many
web APIs), by comparison, are designed in a web-first manner. In this context, property _names_ are important, and general
structures of an API are important, but names of these structures are _not_ important. These APIs are often described and
generated using Swagger, which uses JSON to describe the structure of a response. JSON structures can be strongly typed,
of course: the structure itself is the type. But the nested properties of a JSON object, which can be nested objects
themselves, are described entirely in a structural manner, not in a nominal manner as we do in C#. Here, C#'s paradigms
break down, and the SDK teams run into trouble when creating a C# API to wrap this structure. All of these nested structures
need to be named, so that C# can talk about them. This leads to an explosion of types, which can be made even more difficult
when you consider identical structures developed by different teams (perhaps even different companies). By necessity, each
team will need to create their own "named" data structure to give C# an ability to talk about the object, but these names
are really meaningless. The JSON didn't have this name, and the structures cannot unify. There are also scenarios with
very similar objects (perhaps one object has an extra field that the former does not have). This necessitates an entirely
new object to be created, and users often end up needing to write boilerplate methods that just translate objects from
representation A to B, changing nothing about the data other than making sure the type's name lines up.
This set of scenarios is not addressed by roles and extension methods, as they currently stand. We theorized that teams
might be able to a combination of `dynamic` and a custom completion provider/analyzer, to give users help in writing and
validating code that is, in essence, a set of dynamic calls to nested properties of unnamed types that does have a structure,
but this is a complicated solution to the scenario that is likely not generally-leverageable. There are more IDEs than just
VS, and more IDE scenarios than just completion: what would go-to-def do on these, or quick info on hover?
#### Data Processing
Finally, we took a look at a small proof-of-concept library exploring what replicating parts of the popular Python library
Pandas could be like in C#, with stronger typing around the data generated from a given input. This scenario is very
reminiscent of F# type providers, allowing users to simply dot into a data structure. However, it suffers from the same
set of issues that affect the Azure SDK scenarios above. In order to talk about nested data structures, they have to be
named. And while the Azure examples entirely focus on the types of structures representable in JSON, Pandas is far more
flexible. Additionally, Pandas allows you create new objects as they flow through a pipeline, adding or removing properties
as they are manipulated.
Looking at these last two examples, it seems that there are some scenarios not served well by C# today, involving JSON or
other structured but unnamed data. These scenarios care deeply about the names of properties, and the structure attached to
each property name, but the domains these scenarios interact with do not care about naming these structures. Further, adding
names to these structures in C# can be harmful, because it locks out scenarios that can be accomplished in the original
domain and forces a naming structure where none was intended, which can result in confusing or badly-named types that can
then never be changed because of backwards compatibility concerns. As we continue to evolve the roles and extension
everything proposals, we should look at these scenarios and see if there are ways to improve the experience for them.

View file

@ -1,30 +0,0 @@
# C# Language Design Meeting for December 7th, 2020
## Agenda
1. [Required Properties](#required-properties)
## Quote(s) of the Day
- "I also can't see anyone's video, so raise your hand [in Teams] if you're not here."
- "If you can't solve required properties, you're not making a time machine."
## Discussion
### Required Properties
https://github.com/dotnet/csharplang/discussions/4209
Today, we took a look at the next revision of the required properties proposal, after a few months of design work from a smaller
team to flesh out the design. We had a small questions coming out of the meeting:
* Could assignments in the nominal parameter list always imply `base.`? It would make it easier for automatically considering
hidden properties being initialized.
* We could make it more user friendly by possibly adding warning when a property that is required by the constructor is definitely
assigned in the constructor?
* There's still some debate as to this should only be a source-breaking change.
* Is `init` the right word? Maybe `requires` would be better?
More generally, the reaction to this in the LDM was mixed. While we believe that this is the best proposal we've seen to date, it's
very complicated and introduces a bunch of new concepts. We may need to start looking at simplifying scenarios and seeing whether that
allows us to cut this proposal down a bit.

View file

@ -1,94 +0,0 @@
# C# Language Design Meeting for December 14th, 2020
## Agenda
- [List Patterns](#list-patterns)
## Quote of the Day
- "My Monday is your Friday... you're going to get unadulterated truth here"
## Discussion
### List Patterns
https://github.com/dotnet/csharplang/pull/3245
Today, we took our first in-depth look at list patterns, which will be the next big turn of the crank in generalized pattern
support in C#. The intent of this type of pattern is to allow matching on arbitrary collection types, matching against both
the length and the content of the collection in a single, simple-to-understand fashion, much like our other pattern forms enable
for positional and nominal content. To bring context to the discussion around our general goals and principles for pattern
matching in C#, we again brought up the table from discussion [3107](https://github.com/dotnet/csharplang/discussions/3107):
| Type | Declaration | Creation | Decomposition | Dispatch (pattern) |
|-|:-:|:-:|:-:|:-:|
|`int`|`int x`|`3`|`int x`|`int x` or `3`
|class with mutable fields|`class Point { public int X, Y; }`|`new Point { X = 3, Y = 4 }`|`p.X`|`Point { X: int x, Y: int y }` or `Point { X: 3, Y: 4 }`
|anonymous type|-|`new { X = 3, Y = 4 }`|`p.X`|`{ X: int x, Y: int y }` or `{ X: 3, Y: 4 }`
|record class|`class Point(int X, int Y);` (proposed)|`Point(3, 4)` (proposed)|`(int x, int y) = p`|`Point(int x, int y)` or `Point(3, 4)`
|tuple|`(int x, int y)`|`(3, 4)`|`(int x, int y) = p`|`(int x, int y)` or `(3, 4)`
|List|`List<int>`|`new List<int> { 3, 4 }`| ? | **List patterns fit in here**
|Dictionary|`Dictionary<K,V>`|`new Dictionary<K,V> { { K, V } }`| ? | ?
While we are not looking at list decomposition with this proposal, we should keep it in mind, as we will want whatever form
we use for pattern dispatch to be used for decomposition as well. In this table, we can see a correspondence between the different
syntactic forms of creation and destructuring: object initializers correspond with recursive object patterns, tuple literals
correspond with tuple patterns, etc. By this principle, our initial instinct is to make the collection pattern correspond with
the collection initializer syntax, which uses curly braces. However, this runs into an immediate issue: `{ }` is already a
valid pattern today, the empty object pattern. This means it cannot serve as the empty collection pattern, as that pattern
must specifically check that the collection is actually empty. The current proposal instead takes the approach of using square
brackets `[]` to represent a collection pattern, instead of using the curlies. We're pretty divided on this approach: C# has
not used square brackets to represent a list or array in the past. Even C# 1.0 used the curly brackets for array initializers,
reserving the brackets for array size or index access. This would make the proposed syntax a really big break with C# tradition.
We could "retcon" this by enabling new types of collection literals using square brackets, but that's an issue that LDM has
not intensely looked at beyond previously rejecting https://github.com/dotnet/csharplang/issues/414 and related issues. After
some discussion, we've come to the realization that the empty collection (ie, the base case for recursive algorithms) is the
most important pattern to design for, and the rest of the syntax falls out from that design. We've come up with a few different
syntactic proposals:
1. The existing proposal as is. Notably, this pattern form is _not_ part of a recursive pattern, and that means that you can't
specify a pattern like this: `int[] [1, 2, 3]`. Indeed, such a pattern is potentially ambiguous, as `int[] []` already means
a type test against a jagged `int` array today. Instead, such a pattern would have to be expressed as `int[] and []`. The
first part narrows the type to `int[]`, and the second part specifies that the array must be empty. We're not huge fans of needing
the `and` combinator for a base case (when the input type to the pattern is not narrowed enough to use a collection pattern)
given that one is not needed for tuple deconstruction patterns or property patterns, but it is elegant in its simplicity.
2. A similar version to 1, except that it allows nesting the square brackets inside the curly braces of the property pattern.
This would allow `int[] { [1, 2, 3] }` for the case where you need to both narrow the input type and test the array content.
There are some concerns with this syntax: we've also envisioned a dictionary pattern, that would match content using a form
like this: `{ [1]: { /* some nested pattern */ } }`. This would mean that the colon at the end of the brackets would determine
whether the contents of the brackets are used as arguments to an indexer or the patterns the collection is being tested against.
3. Using curlies to match the list contents. There are a couple of sub proposals in this section, separated by the way they
enable testing for the empty collection case. They share the content tests, which look like `{ 1, 2, 3 }`.
1. No empty case. Instead, use a property pattern on `Length` or `Count` to check for the empty case. This has issues
with our previously-desired support for `IEnumerable` and general `foreach`-able type support, as they do not have any
such property to check.
2. Empty case represented by a single comma: `{,}` would represent an array with `Length == 0`. This was suggested, but
no one argued in favor.
3. Square brackets for `Length` tests. This proposal would look something like this: `int[] [0]`. The interesting angle
with this version is that it allows for succinct length tests that could be composed of patterns itself. For example, the
BCL has some cases where they need to check to see whether an array has some content and Length between two cases, and that
could be expressed as `[>= 0 and < 256] { 1, 2, .. }`. This would also allow a general length check to be expressed for
`foreach`-able types, though there are some concerns that it would become a perf pitfall if enumerating the entire sequence
was necessary to check a non-zero length. The length or count of the collection could also be extracted with a declaration
pattern, which could turn into a nice shorthand for not having to know whether this collection uses `Length` or `Count`,
something we didn't standardize and now can't. How this version combines with other property tests on the same object would
still need to be discussed: could you do `MyType { Property: a } [10] { 1, 2, 3, .. }`, for example, or would the property and
collection patterns need to be combined with an `and`?
4. Add a new combinator keyword to make the transition to a collection pattern explicit. This is similar to how VB uses
`From` to indicate collection initializers. Such a pattern might look something like `int[] with { }` for the empty case.
(`with` was the word we spitballed here, but likely wouldn't end up being the final word for confusion with `with` expressions).
We came to no solid conclusions on the syntax topic today, as we were mostly generating ideas and need some time to mull over the
various forms. We'll come back to this at a later date.
We also took a brief look at the slice pattern and whether it could be extended to `foreach`-able types. A trailing `..` in a
`foreach` match would be easy to implement and not have any hidden costs, as it would just skip a check to `MoveNext()` after
the leading bits of the pattern match. However, a leading `..` would be much more concerning. Depending on implementation
strategy, we'd have emit a much larger state machine or keep track of a potentially large number of previous values as we
iterate the enumerable, so that when we get to the end we can ensure that the previous slots matched correctly. We're not
sure if this difference will be obvious enough to users, and will need to think more about whether we should enable the trailing
slice, or enable both slice patterns and let the codegen be what it will. In all likelihood, if the user needs this pattern
they're going to code it by hand if they can't do it with a pattern, and we can make it less likely to introduce a bug for it
if we generate the states programmatically instead of the user doing it by hand.
Again, we came to no solid conclusions here, as we spent most of our time on the syntax aspects.

View file

@ -1,87 +0,0 @@
# C# Language Design Meeting for December 14th, 2020
## Agenda
1. [List patterns](#list-patterns)
2. [Definite assignment changes](#definite-assignment-changes)
## Quote of the Day
- "They are syntax forms you little devil. No amount pedanticism is too much in C#."
## Discussion
### List patterns
https://github.com/dotnet/csharplang/pull/3245
Coming out of [Monday's meeting](LDM-2020-12-14.md), we had a few different competing proposals for syntax. As a quick recap:
1. The original proposal as is.
2. Put the brackets from 1 inside the braces on the top level.
3. Use braces for the list pattern, with the empty case being:
1. No empty case.
2. `{,}`
3. Add a `[pattern]` form that allows testing and potentially extracting the length of a collection.
4. Add a new combinator to make the braces explicitly a list pattern, which would allow `{ }` to be the base case.
After the notes were published, we took the list and had an email discussion to narrow in on the specifics of each of these cases.
Cases 2, 3.i, 3.ii, and 3.iv were not defended in this email chain, and coming into today's meeting there were 4 different main syntax
proposals, the final 3 being variations of 3.iii from the original list (in psuedo-antlr):
1. The original proposal. This introduces a new `pattern`, which uses `'[' (pattern (',' pattern)* ']')` as the syntax of that
new pattern. This cannot be expressed as a top-level concept in a `positional_pattern` or a `property_pattern` because the braces
can be ambiguous with the `type` component of these patterns.
2. `type? ('(' subpatterns? ')')? ('[' pattern ']')? ('{' property_subpatterns_or_list_element_patterns '}')?`.
This form modifies the `positional_pattern` syntax introduced in C# 8 to add a length pattern section, defined by the middle
`[pattern]` section, and modifies the final braces to contain either a set of positional subpatterns, or a set of list element
patterns, but not both. To test both property elements and list elements, a conjunctive pattern needs to be used.
3. `type? ('(' subpatterns? ')')? ('[' pattern ']')? ('{' property_subpatterns '}')? ('{' list_subpatterns '}')?`.
This is very similar to 2, except that it allows both property and list subpatterns at the same time.
4. `type? ('(' subpatterns? ')')? ('[' pattern ']')? ('{' property_subpatterns_and_list_element_patterns '}')?`.
This is very similar to 2, except that it allows both property subpatterns and list subpatterns in the same set of braces. Consider
subproposals of this version to require properties first, list elements first, or no ordering requirements.
The very important goal for the language team here is to follow the correspondence principle. That means that if you construct using one
syntax construct, you should use the same construct to deconstruct. For collection types, this means that we strongly want to prefer
using curly braces as the deconstruction syntax, rather than square brackets, because collection initializers use the braces. It is
possible that at some point in the future, we could add a collection literal syntax that uses square brackets, but there is strong
history in C# to avoid using the brackets in this fashion. Up to this point, the brackets have always contained indexes or lengths
in the language, and lists of things to initialize have always been inside braces. Changing that at this point, even if we later seek
to add conformance by introducing a new collection literal, would be asking C# users to unlearn a concept that has been unchanged
since C# 1.0, which is very concerning to us. Given this desire, option 1 deviates too much from existing C#, and we will instead
focus on one of the latter options.
Of these latter options, option 2 can be viewed as a strict subset of both 3 and 4, as either will allow using conjunctive patterns
to separate out the list and property patterns if users feel that the combination is unreadable. Additionally, we again turn to the
correspondence principle: today, you cannot combine both collection initializers and object initializers. By the correspondence
principle, then, you should not be able to combine them in the same pattern during deconstruction. We're not necessarily opposed to
allowing collection and object initializers to be combined in the future, but that is out of scope for the collection pattern changes.
Finally, in discussions Monday and over email, we also took a brief look at indexer patterns as possible `property_subpatterns`. These
would look something like this: `{ [1]: pattern, [2..^4]: var slice }`. This form seems like a good next step after list patterns to
allow deconstructioning objects with indexers. These indexer arguments could allow non-integer constants as well as multiple arguments,
giving a deconstruction mechanism for dictionaries that corresponds to object initializers in this area.
#### Conclusion
We'll move forward with the general syntax proposed in option 2, with a length subpattern and allowing either property subpatterns or
list subpatterns in the same "recursive pattern" section. We still need to translate the psuedo-spec above into a formal specification.
We also did not address the open questions around whether the length pattern should be applicable to types of `IEnumerable`, and if so
whether `[0]` is the only allowed pattern or if any pattern is allowable.
### Definite assignment changes
https://github.com/dotnet/csharplang/discussions/4240
This is an area of longstanding pain for C# users: any time conditional evaluation and comparison to constants mix, definite assignment
cannot figure out what is going on and variables that the user can see are obviously assigned are not considered assigned. We're highly
in support of this idea in general, as everyone has run into this at some point or another in their C# careers. The definite assignment
rules are written in a very syntax-driven form, and thus this proposal is written in a very syntax-driven form to update the relevant
constructs. Despite that, we do wonder whether we can make these rules more holistic and systematic, such that we don't need to make
them syntax-specific like they need to be today. We're also less enthused about the conditional expression version. If it fell out of
more general rules it would be nice, but it's not highly important like the null conditional and coalescing changes seem to be.
#### Conclusion
The general idea is approved. We'll work to see if we can generalize the rules a bit, and submit a proposal for review on github.

View file

@ -1,452 +0,0 @@
# C# Language Design Notes for 2020
Overview of meetings and agendas for 2020
## Dec 16, 2020
[C# Language Design Notes for December 16th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-12-16.md)
- List patterns
- Definite assignment changes
## Dec 14, 2020
[C# Language Design Notes for December 14th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-12-14.md)
- List patterns
## Dec 7, 2020
[C# Language Design Notes for December 7th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-12-07.md)
- Required Properties
## Dec 2, 2020
[C# Language Design Notes for December 2nd, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-12-02.md)
- Partner scenarios in roles and extensions
## Nov 16, 2020
[C# Language Design Notes for November 16th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-11-16.md)
- Triage
## Nov 11, 2020
[C# Language Design Notes for November 11th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-11-11.md)
- IsRecord in metadata
- Triage
## Nov 4, 2020
[C# Language Design Notes for November 4th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-11-04.md)
- Nullable parameter defaults
- Argument state after call for AllowNull parameters
## Oct 26, 2020
[C# Language Design Notes for October 26st, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-10-26.md)
- Pointer types in records
- Triage
## Oct 21, 2020
[C# Language Design Notes for October 21st, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-10-21.md)
- Primary Constructors
- Direct Parameter Constructors
## Oct 14, 2020
[C# Language Design Notes for October 14th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-10-14.md)
- Triage
- Milestone Simplification
## Oct 12, 2020
[C# Language Design Notes for October 12th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-10-12.md)
- General improvements to the `struct` experience (continued)
## Oct 7, 2020
[C# Language Design Notes for October 7th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-10-07.md)
- `record struct` syntax
- `data` members redux
- `ReadOnlySpan<char>` patterns
## Oct 5, 2020
[C# Language Design Notes for October 5th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-10-05.md)
- `record struct` primary constructor defaults
- Changing the member type of a primary constructor parameter
- `data` members
## Sep 30, 2020
[C# Language Design Notes for September 30th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-09-30.md)
- `record structs`
- `struct` equality
- `with` expressions
- Primary constructors and `data` properties
## Sep 28, 2020
[C# Language Design Notes for September 28th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-09-28.md)
- Warning on `double.NaN`
- Triage
## Sep 23, 2020
[C# Language Design Notes for September 23rd, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-09-23.md)
- General improvements to the `struct` experience
## Sep 16, 2020
[C# Language Design Notes for September 16th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-09-16.md)
- Required Properties
- Triage
## Sep 14, 2020
[C# Language Design Notes for September 14th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-09-14.md)
- Partial method signature matching
- Null-conditional handling of the nullable suppression operator
- Annotating IEnumerable.Cast
- Nullability warnings in user-written record code
- Tuple deconstruction mixed assignment and declaration
## Sep 9, 2020
[C# Language Design Notes for September 9th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-09-09.md)
- Triage issues still in C# 9.0 candidate
- Triage issues in C# 10.0 candidate
## Aug 24, 2020
[C# Language Design Notes for August 24th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-08-24.md)
- Warnings on types named `record`
- `base` calls on parameterless `record`s
- Omitting unnecessary synthesized `record` members
- [`record` `ToString` behavior review](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/records.md#printing-members-printmembers-and-tostring-methods)
- Behavior of trailing commas
- Handling stack overflows
- Should we omit the implementation of `ToString` on `abstract` records
- Should we call `ToString` prior to `StringBuilder.Append` on value types
- Should we try and avoid the double-space in an empty record
- Should we try and make the typename header print more economic
- Reference equality short circuiting
## Jul 27, 2020
[C# Language Design Notes for July 27th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-07-27.md)
- [Improved nullable analysis in constructors](https://github.com/RikkiGibson/csharplang/blob/nullable-ctor/proposals/nullable-constructor-analysis.md) (Rikki)
- [Equality operators (`==` and `!=`) in records](https://github.com/dotnet/csharplang/issues/3707#issuecomment-661800278) (Fred)
- `.ToString()` or `GetDebuggerDisplay()` on records? (Julien)
- Restore W-warning to `T t = default;` for generic `T`, now you can write `T?`? (Julien)
## Jul 20, 2020
[C# Language Design Notes for July 20th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-07-20.md)
- [struct private fields in definite assignment](https://github.com/dotnet/csharplang/issues/3431) (Neal/Julien)
- [Proposal 1](https://github.com/dotnet/roslyn/issues/30194#issuecomment-657858716)
- [Proposal 2](https://github.com/dotnet/roslyn/issues/30194#issuecomment-657900257)
- Finish [Triage](https://github.com/dotnet/csharplang/issues?q=is%3Aopen+is%3Aissue+label%3A%22Proposal+champion%22+no%3Amilestone)
- Records-related features to pick up in the next version of C# (Mads)
## Jul 13, 2020
[C# Language Design Notes for July 13th, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-07-13.md)
- Triage open issues
## Jul 6, 2020
[C# Language Design Notes for July 6, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-07-06.md)
- [Repeat Attributes in Partial Members](https://github.com/RikkiGibson/csharplang/blob/repeated-attributes/proposals/repeat-attributes.md) (Rikki)
- `sealed` on `data` members
- [Required properties](https://github.com/dotnet/csharplang/issues/3630) (Fred)
## Jul 1, 2020
[C# Language Design Notes for July 1, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-07-01.md)
- [Non-defaultable struct types](https://github.com/dotnet/csharplang/issues/99#issuecomment-601792573) (Sam, Chuck)
- Confirm unspeakable `Clone` method and long-term implications (Jared/Julien)
## Jun 29, 2020
[C# Language Design Notes for June 29, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-06-29.md)
- [Static interface members](https://github.com/Partydonk/partydonk/issues/1) (Miguel, Aaron, Mads, Carol)
## Jun 24, 2020
[C# Language Design Notes for June 24, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-06-24.md)
- Parameter null checking: finalize syntax
- https://github.com/dotnet/csharplang/issues/3275 Variance on static interface members (Aleksey)
- [Function pointer question](https://github.com/dotnet/roslyn/issues/39865#issuecomment-647692516) (Fred)
## Jun 22, 2020
[C# Language Design Notes for June 22, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-06-22.md)
1. Data properties
1. Clarifying what's supported in records for C# 9
- Structs
- Inheritance with records and classes
## Jun 17, 2020
[C# Language Design Notes for June 17, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-06-17.md)
1. Null-suppression & null-conditional operator
1. `parameter!` syntax
1. `T??`
## Jun 15, 2020
[C# Language Design Notes for June 15, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-06-15.md)
Record:
1. `modreq` for init accessors
1. Initializing `readonly` fields in same type
1. `init` methods
1. Equality dispatch
1. Confirming some previous design decisions
1. `IEnumerable.Current`
## Jun 10, 2020
[C# Language Design Notes for June 10, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-06-10.md)
- https://github.com/dotnet/csharplang/issues/1711 Roles and extensions
## Jun 1, 2020
[C# Language Design Notes for June 1, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-06-01.md)
Records:
1. Base call syntax
2. Synthesizing positional record members and assignments
3. Record equality through inheritance
## May 27, 2020
[C# Language Design Notes for May 27, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-05-27.md)
Record syntax
1. Record structs?
2. Record syntax/keyword
3. Details on property shorthand syntax
## May 11, 2020
[C# Language Design Notes for May 11, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-05-11.md)
Records
## May 6, 2020
[C# Language Design Notes for May 6, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-05-06.md)
1. Target-typing ?: when the natural type isn't convertible to the target type.
1. Allow `if (x is not string y)` pattern.
1. Open issues in extension `GetEnumerator`
1. Args in top-level programs
## May 4, 2020
[C# Language Design Notes for May 4, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-05-04.md)
1. Reviewing design review feedback
## April 27, 2020
[C# Language Design Notes for April 27, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-04-27.md)
Records: positional & primary constructors
## April 20, 2020
[C# Language Design Notes for April 20, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-04-20.md)
Records: Factories
## April 15, 2020
[C# Language Design Notes for April 15, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-04-15.md)
1. Non-void and non-private partial methods
2. Top-level programs
## April 13. 2020
[C# Language Design Notes for April 13, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-04-13.md)
1. Roadmap for records
2. Init-only properties
## April 8, 2020
[C# Language Design Notes for April 8, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-04-08.md)
1. `e is dynamic` pure null check
2. Target typing `?:`
3. Inferred type of an `or` pattern
4. Module initializers
## April 6, 2020
[C# Language Design Notes for April 6, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-04-06.md)
1. Record Monday: Init-only members
## April 1, 2020
[C# Language Design Notes for April 1, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-04-01.md)
1. Function pointer design adjustments
2. `field` keyword in properties
## March 30, 2020
1. Record Monday
[C# Language Design Notes for March 30, 2020](LDM-2020-03-30.md)
## March 25, 2020
[C# Language Design Notes for March 25, 2020](LDM-2020-03-25.md)
1. Open issues with native int
2. Open issues with target-typed new
## March 23, 2020
[C# Language Design Notes for March 23, 2020](LDM-2020-03-23.md)
1. Triage
2. Builder-based records
## March 9, 2020
[C# Language Design Notes for March 9, 2020](LDM-2020-03-09.md)
1. Simple programs
2. Records
## Feb 26, 2020
[C# Language Design Notes for Feb. 26, 2020](LDM-2020-02-26.md)
Design Review
## Feb 24
[C# Language Design Notes for Feb. 24, 2020](LDM-2020-02-24.md)
Taking another look at "nominal" records
## Feb 19
[C# Language Design Notes for Feb. 19, 2020](LDM-2020-02-19.md)
State-based value equality
## Feb 12
[C# Language Design Notes for Feb. 12, 2020](LDM-2020-02-12.md)
Records
## Feb 10
[C# Language Design Notes for Feb. 10, 2020](LDM-2020-02-10.md)
Records
## Feb 5
[C# Language Design Notes for Feb. 5, 2020](LDM-2020-02-05.md)
- Nullability of dependent calls (Chuck, Julien)
- https://github.com/dotnet/csharplang/issues/3137 Records as individual features (Mads)
## Feb 3
[C# Language Design Notes for Feb. 3, 2020](LDM-2020-02-03.md)
Value Equality
## Jan 29, 2020
[C# Language Design Notes for Jan. 29, 2020](LDM-2020-01-29.md)
Records: "With-ers"
## Jan 22, 2020
[C# Language Design Notes for Jan 22, 2020](LDM-2020-01-22.md)
1. Top-level statements and functions
2. Expression Blocks
## Jan 15, 2020
[C# Language Design Notes for Jan 15, 2020](LDM-2020-01-15.md)
Records
1. "programming with data"
1. Decomposing subfeatures of records
## Jan 8, 2020
[C# Language Design Notes for Jan 8, 2020](LDM-2020-01-08.md)
1. Unconstrained type parameter annotation
2. Covariant returns
## Jan 6, 2020
[C# Language Design Notes for Jan 6, 2020](LDM-2020-01-06.md)
1. Use attribute info inside method bodies
1. Making Task-like types covariant for nullability
1. Casting to non-nullable reference type
1. Triage

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 MiB

View file

@ -1,48 +0,0 @@
# C# Language Design Meeting for Jan. 5th, 2021
## Agenda
1. [File-scoped namespaces](#file-scoped-namespaces)
## Quote of the Day:
- "I see a big tropical void where [redacted's] face was... It's so annoying"
- "It's so cold here, it's 70 [F]"
## Discussion
### File-scoped namespaces
https://github.com/dotnet/csharplang/issues/137
Today, we looked at some of the details around this feature, specifically what should be supported before or after a top-level
namespace declaration. The proposal as written allows only extern aliases, using directives, and global attributes. The proposal
also does not allow multiple top-level namespaces: you can only have one in the file, and all following type declarations are
considered to be part of that namespace. The debate, therefore, centers on whether we allow multiple of these declarations in a
file, what they would mean in that case, and whether we allow a top-level namespace at the same file as top-level statements.
In many ways, this seems like a style question. Syntactically, regardless of whether allow these concepts to be mixed/duplicated
in a single file in the formal grammar, the compiler will have to implement rules for what this means in order to provide a good
IDE experience. There is potential value is allowing this to be flexible, as we generally do not take strong stances on syntax
formatting guidelines beyond the default rules shipped with Roslyn, and those are very customizable to allow users to decide
whether to allow them or not (`var` vs explicit type has 3 major doctrines, for example). By allowing all forms here, we would
let users decide what is preferred to them and what is not.
That being said, however, we have concerns that we even understand what code like this would do:
```cs
namespace X;
class A {}
namespace Y;
class B {}
```
For this scenario, some people would expect these types to be `X.A` and `Y.B`, while others would expect them to be `X.A` and
`X.Y.B`. We have additional concerns around how this type of code would read in the presence of top-level statements, and
whether there would be enough visual contrast between the end of the top-level statements, the namespace, and then types under
the namespace, or whether that would be confusing to read. If we restrict the usage now, nothing would stop us from loosening
restrictions in a later language version if we discover that we were too restrictive initially, but if we let the genie out of
the bottle now, we can never put it back in.
#### Conclusion
No conclusion today. We're largely split between these two extremes, allowing everything or allowing nothing. We'll take this
back up again soon to finish debate and settle on a conclusion.

View file

@ -1,65 +0,0 @@
# C# Language Design Meeting for Jan. 11th, 2021
## Agenda
1. [Required properties simple form](#required-properties-simple-form)
## Quote of the Day
- "You didn't want to hear me say um anyway... So, um"
## Discussion
### Required properties simple form
https://github.com/dotnet/csharplang/discussions/4209#discussioncomment-275575
Today, we took a look at an extension to the existing required properties proposal, that proposed a syntax form
that simplifies the base case of the proposal to remove complexity for what we believe is the 80% use case. This
form adds a `require` modifier on a property definition. These requirements and then automatically added to the
implicit parameterless constructor of a type, if present, and can be added to explicit constructors with
`require default`, in the same place as the `init` lists of the last proposal.
First, we discussed the syntax in the proposal, and potential alternatives. We like the move to put a modifier
on properties and fields as it makes implicit constructor scenarios much simpler, but something still feels off
about the full-blown form of this syntax, with `require { Property, List }`. We could draw on type parameter
constraint clauses, and a rough first attempt looks promising:
```cs
public Person() require FirstName, LastName
{
}
public Student() require ID
: base()
{
}
```
The proposed ability to assign to properties in the require list might be either odd or outright impossible here,
depending on potential syntactic ambiguities we haven't thought of yet, but the syntax immediately feels more
like C# than the previous curly-brace version.
Where we spent the bulk of meeting, however, was on how implicit we can make the default parameter list. We
had immediate pushback on `require default` even being necessary on constructors: why can't we just infer that
for all constructors in a type, and then have a syntax for removing all requirements? There's a feeling that
`require default` is just busywork, and the compiler should just infer the defaults from the properties and
fields in the type that are marked `require`. Some proposals for the ability to remove all requirements are
`require none` and `require default = _`. We also considered a version of the proposal that goes even further,
that doesn't allow constructors to `require` additional items: you mark a property or field as required, then
remove requirements in the constructor itself. In this model, constructors would be unable to add new requirements,
which does remove some potential scenarios, but could simplify the feature significantly. Roughly speaking, the
three versions of the proposal can be summarized as follows:
1. Only implicit constructors get implicit require lists.
2. All constructors get implicit require lists, and can add requirements of their own:
1. If the constructor calls base in some manner (including implicit calls to the `object` constructor), that
list is all the fields and properties in the type that are marked require.
2. If the constructor calls another constructor on `this`, then it simply advertises chaining to that
constructor, potentially removing some requirements if it takes care of them in its body.
3. All constructors get implicit require lists, and cannot add to them. They can only remove them, and there
is no `require` syntax. This version will need a new syntax for removing requirements, but that will likely
be much simpler than the full `require` clause and need less user education.
After a read of the room, we're interested in seeing where proposal 3 goes. We'll work on fleshing that out
with examples and bring it back to LDM soon.

View file

@ -1,84 +0,0 @@
# C# Language Design Meeting for Jan. 13th, 2021
## Agenda
1. [Global usings](#global-usings)
2. [File-scoped namespaces](#file-scoped-namespaces)
## Quote(s) of the Day
- "You're not yelling at me. You're just wrong."
- "All language rules are arbitrary. Some are just more arbitrary than others."
## Discussion
### Global usings
https://github.com/dotnet/csharplang/issues/3428
Today, we started by looking at a long-standing request in various forms: the ability to create using directives that apply
to an entire project. This is of particular importance now as we solidify our work in relation to the .NET 6 themes, especially
around both beginner scenarios and general ease-of-use. A well-studied problem in teaching a programming language is avoiding
cognitive load, and usings are an ever-present cognitive load in C#, even in simple "Hello, World!" style applications. Either
the teacher says "Ignore the `using` thing for now" or they say "Ignore what the `.`s mean for now", with no ability to hold
off on introducing them. That's not to say teaching `using` isn't necessary, and necessary early in the teaching process; merely
that delaying that introduction from the first second of seeing C# to a day or week into the curriculum can be very helpful in
avoiding overloading newcomers and scaring them off.
While it's an important scenario, beginners aren't the only driving motivation here. .NET 6 is looking at making both beginner
and general scenarios better, and there's an argument that this will help general scenarios as well. Large-sized projects such
as dotnet/roslyn are the exception, not the rule; most .NET projects are smaller and don't have nearly so many moving and
interacting pieces. `using` boilerplate has a bigger impact on these projects, particularly as they tend to use many frameworks
and a custom project SDK (such as ASP.NET). That custom SDK, combined with a feature to allow the SDK to specify global usings
in some manner, can help ease these scenarios and remove unnecessary lines from most files in such solutions. Larger projects
like Roslyn may never use this feature, but Roslyn and projects like it are not the projects that much of our users are actually
writing.
Broadly, there are two possible approaches to this type of feature: CLI flags, specifiable for actual users via the project file,
and a syntax form that allows a user to specify a using should apply to all files. We have an existing proposal for the former
approach, 3428 (linked above), and some spitball ideas for what the latter could look like (perhaps something like
`global using System;`). Both have advantages:
* If these are specified via command line flags, then there is one place to go looking for them: the project file. A syntax form
would be potentially able to be spread out among multiple files. It is would be possible to spread these out across multiple
props files if users wanted to, but the types of projects that use these features are likely rarer than the types of projects
that use multiple C# files. Tooling could certainly help here, such as creating a new node in the project explorer to list all
the global usings for a project, but we do still need to consider cases where code is not viewed in an IDE such as on GitHub.
* We have a number of long-standing requests for having global using aliases. While these can be accomplished via the CLI flag
proposal, it would be significantly easier and more accessible to users if they had a syntax form of doing so.
* A syntax form would allow participation from source generators. We're somewhat split on whether that's a good thing or not.
* A syntax form might be a barrier to potential abilities to do things like `dotnet run csfile` in the future: where would the
syntax form live? An ethereal temp file, or a hardcoded part of the SDK?
#### Conclusion
We seem to have unanimous agreement here that the design space is interesting, and we would like a feature to address the issues
discussed above. We're much more split on the potential approaches, however, and need to explore the space more in depth.
### File-scoped namespaces
https://github.com/dotnet/csharplang/issues/137
We're picking back up on the discussion from [last week](LDM-2021-01-05.md#file-scoped-namespaces), which is around how
restrictive we should be in permitted mixing and matching of multiple namespace statements and combination with traditional
namespace directives. An important point to acknowledge is that, regardless of what decision we make here, Roslyn is going to
have to do something to understand what multiple namespace directives in a file means because it will encounter that code at
some point, regardless of whether it's valid or not, and will have to do its level best to make a guess as to what the user
meant. There is a big difference between a compiler trying to make as much sense as it can of invalid code and the language
having actual rules for the scenario, though. The scenario we're targeting specifically is files with one namespace in them
(and most often, one type as well), and these scenarios make up roughly 99.8% of C# syntax files that lived on one LDT-member's
computer. This includes the Roslyn codebase, which has several of these types of files specifically for the purposes of
testing that we handle the scenario correctly. Measuring an even broader set of millions of C# files on GitHub shows literally
99.99% of files have just one namespace in them.
We also briefly discussed the interaction with top-level statements. On the one hand, we're concerned about the readability of
combining these things, and that the namespace statement would be too easily missed. On the other hand, having just finished
talking about beginner scenarios, it seems like it might be annoying that beginners couldn't be introduced to the simple form
until they start splitting things apart into multiple classes. Users will likely police themselves here if it doesn't read well,
and maybe restricting it is just adding an arbitrary restriction.
#### Conclusion
We allow one and only one file-scoped namespace per file. You cannot combine a file-scoped namespace with a traditional
namespace directive in the same file. We did not reach a conclusion on the combination with top-level statements and will
pick that up again soon.

View file

@ -1,117 +0,0 @@
# C# Language Design Meeting for Jan. 27th, 2021
## Agenda
1. [Init-only access on conversion on `this`](#init-only-access-on-conversion-on-this)
2. [Record structs](#record-structs)
1. [Copy constructors and Clone methods](#copy-constructors-and-clone)
2. [`PrintMembers`](#printmembers)
3. [Implemented equality algorithms](#implemented-equality-algorithms)
4. [Field initializers](#field-initializers)
5. [GetHashcode determinism](#gethashcode-determinism)
## Quote of the Day
- "You don't see the gazillion tabs I have in my other window... It's actually mostly stackoverflow posts on how to use System.IO.Pipelines."
## Discussion
### Init-only access on conversion on `this`
https://github.com/dotnet/roslyn/issues/50053
We have 3 options on this issue, centered around how whether we want to make a change and, if so, how far do we want to take it.
1. Change nothing. The scenario remains an error.
2. Allow unconditional casts.
3. Allow `as` casts as well.
We feel that this case is pretty pathological, and we have trouble coming up with real-world examples of APIs that would need to
both hide a public member from a base type and initialize it in the constructor to some value. It would also be odd to allow it
in the constructor while not having a form of initializing the property from an object initializer, which is the new thing that
`init` enables over `set` methods. If we continue seeing a need to name hidden members, perhaps we can come up with a feature that
generally allows that, as opposed to solving one particular case in constructors.
#### Conclusion
We'll go with 1. The proposal is rejected.
### Record structs
https://github.com/dotnet/csharplang/issues/4334
#### Copy constructors and Clone methods
We're revisiting the decision made the last time we talked about record structs. In that meeting, we decided to disallow record
structs from defining customized `with` semantics, due to concerns over how such structs would behave in generic contexts when
constrained to `where T : struct`. If we do disallow this customization, do we need to disallow methods named Clone as well?
And should we also disallow copy constructors? In looking at the questions here, we spitballed some potential ways that we could
allow customized copies and still allow record structs to be used in generics constrained to `where T : struct`, potentially by
introducing a new interface in the BCL. A `with` on a struct type param would check for an implementation of that interface and
call that, rather than blindly emitting a `dup` instruction as our original intention was. We think it's an interesting idea and
want to pull it into a complete proposal, so we're holding off on making any decisions about allowed and disallowed members in
record structs related to copying for now.
##### Conclusion
On hold.
#### `PrintMembers`
A question was raised during initial review of the specification for record structs on whether we need to keep `PrintMembers`.
Struct types don't have inheritance, so we could theoretically simplify that to just `ToString()` for this case. However, we
think that there is value in minimizing the differences between record structs and record classes, so conversion between them
is as painless as possible. Since users can provide their own `PrintMembers` method with their own semantics, removing it
potentially introduces friction in making such a change.
##### Conclusion
Keep `PrintMembers`. The behavior will be the same as in record classes.
#### Implemented equality algorithms
During review, a question was raised about our original decision here with respect to generating the actual equality implementation
for record structs. Originally, we had decided to not generate a new `Equals(object)` method, and have making a struct a record
be solely about adding new surface area for the same functionality. Instead, we'd work with the runtime to make the equality
generation better for all struct types. While we still want to pursue this angle as well, after discussion we decided that this
would be another friction point between record classes and record structs, and could potentially have negative consequences if
we don't have time to ship better runtime equality for structs in .NET 6, as many scenarios would then just need to turn around
and implement equality themselves. In the future, if we do get the better runtime-generated equality, that could be added as a
feature flag to `System.Runtime.CompilerServices.RuntimeFeature`, and we can inform the generation of equality based on the
presence of the flag.
##### Conclusion
We will generate equality methods in the same manner as proposed in the record struct specification proposal.
#### Field initializers
The question here is whether we can allow field initializers in structs that have a primary constructor with more than zero
parameters. The immediate followup to that question, of course, is can we just finally allow parameterless constructors for
structs in general, and then field initializers just work for all of them? We're still interested in doing this: the
`Activator.CreateInstance` bug was in a version of the framework that is long out of support at this point, and we have universal
agreement behind the idea. The last time we talked about the feature we took a look at non-defaultable value types in general,
and while there are interesting ideas in there, we don't think we need to block parameterless constructors on it.
##### Conclusion
Let's dig up the proposal from when we did parameterless struct constructors last and get it done, then this question becomes
moot.
#### GetHashcode Determinism in `Combine`
The record class specification, and the record struct specification, states:
> The synthesized override of `GetHashCode()` returns an `int` result of a deterministic function combining the values of
> `System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN)` for each instance field `fieldN` with `TN` being
> the type of `fieldN`.
We're not precise on the semantics of "a deterministic function combining the values" here, and the question is whether we should
be more precise about the semantics of that. After discussion, we believe we're fine with the wording. It does not promise
determinism across boundaries such as different executions of the same program or running the same program on different versions
of the runtime, which are not guarantees we want to make.
##### Conclusion
Fine as is.

View file

@ -1,78 +0,0 @@
# C# Language Design Meeting for Feb 3rd, 2021
## Agenda
1. [List patterns on `IEnumerable`](#list-patterns-on-ienumerable)
2. [Global usings](#global-usings)
## Quote of the Day
- "If the teacher doesn't show up, class is dismissed, right?" "Who is the teacher in this scenario?"
## Discussion
### List patterns on `IEnumerable`
https://github.com/alrz/csharplang/blob/list-patterns/proposals/list-patterns.md
Today, we discussed what the behavior of list patterns should be for `IEnumerable`, and specifically how much of list patterns
should be able to operate on enumerables. There are multiple competing factors here that we need to take into consideration.
1. `IEnumerable`s can be infinite. If a user were to attempt to match the count of such an enumerable, their code hangs.
2. `IEnumerable`s can be expensive. Likely more common than the infinite case, enumerable can be a DB query that needs to run
off to some data server in the cloud when queried. We absolutely do not want to enumerate these multiple times.
3. We do not want to introduce a new pitfall of "Oh, you're in a hot loop, remove that pattern match because it'll be slower
than checking the pattern by hand".
All of that said, we do think that list patterns on enumerables are useful. While this can be domain specific, efficient enumeration
of enumerables is relatively boilerplate code and with some smart dependence on framework APIs, we think there is a path forward.
For example, the runtime just approved a new API for [`TryGetNonEnumeratedCount`](https://github.com/dotnet/runtime/issues/27183),
and in order to make the pattern fast we could attempt to use it, then fall back to a state-machine-based approach if the collection
must be iterated. This would give us the best of both worlds: If the enumerable is actually backed by a concrete list type, we don't
need to do any enumeration of the enumerable to check the length pattern. If it's not, we can fall back to the state machine, which
can do a more efficient enumeration while checking subpatterns than we could expose as an API from the BCL.
For the state machine fallback, we want to be as efficient as possible. This means not enumerating twice, and bailing out as soon
as possible. So, the pattern `enumerable [< 6] { 1, 2, 3, .., 10 }` can immediately return false if it gets to more than 6 elements,
or if any of the first 3 elements don't match the supplied patterns.
Finally, on the topic of potentially infinite or expensive enumerations, they are an existing problem today. The BCL exposes a `Count`
API, and if you call it on a Fibonacci sequence generator, your program will hang. Enumerating db calls is expensive, regardless
of whether we provide a new, more succinct form or not. In these cases, users generally know what they're working with: it's not a
surprise that they have an infinite enumerable, they've very likely already done a `Take` or some other subsequence mechanism if they're
looking for "the last element from the end". By having these patterns, we simply allow these users to take advantage of a generation
strategy that's as efficient as they could write by hand, with much clearer intent. As long as the enumeration has a specific pattern
that users can reason about, it's an overall win.
#### Conclusion
We'll proceed with making a detailed specification on how `IEnumerable` will be pattern matched against. We're ok with taking advantage
of BCL APIs here, including `TryGetNonEnumeratedCount`, and are comfortable working with the BCL team to add new APIs if existing ones
don't prove complete enough for our purposes.
### Global usings
We started this by looking at a prototype of how ASP.NET is looking to reduce ceremony in their templates with a framework called
Feather, which can be seen [here](https://github.com/featherhttp/framework). The hello world for this code is 12 lines long: 6 lines
of actual code, 3 newlines, and 3 lines of usings. As apps get more complicated, these usings tend to grow quite quickly, and they're
all for the types of things that often boil down to "I want to use the async feature from C# 5, LINQ from C# 3, generic collections
from C# 2, and I want to build an ASP.NET application". This hints at a related, but orthogonal, using feature: recursive usings. For
example, `using System.*` would bring in all namespaces under `System`, or `using Microsoft.AspNetCore.*` would bring in all namespaces
under `Microsoft.AspNetCore`. However, such a feature wouldn't really solve the issue in question here, which is "how can specifying
the SDK in use ensure that I get the ability to use the features of that SDK by default?"
We have 2 general approaches here: use the project file as the place where implicit usings go, or allow a source file to include them.
Both approaches have several pros and cons. In a project file works more natively for an SDK, as they can just define a property. The
SDK does define an AssemblyVersion.cs today, but this feature is potentially more complicated than that. The project file is also
where we tend to put these types of global controls, like nullable or checked. On the other hand, project files are very hard to tool,
as MSBuild is a complicated beast that can do arbitrary things. Artificial restrictions on the feature, like requiring that it appear
directly in the project file and not in some other targets file, severely limits the usefulness of the feature across solutions. Source
files as the solution provide an easily-toolable experience that feels more C#-native, but potentially encourages these usings to be
spread out in many locations. Razor has a `_ViewImports.cshtml` file that handles this problem for Razor files, but we don't think this
maps well to the solutions we're discussing for C#: it only allows the one file, and is in some ways the "project file" for the rest
of the cshtml files in the solution as it provides things like the namespace of the rest of the pages.
#### Conclusion
We're split right down the middle here between project file and C# files. We'll revisit this again very shortly to try and make
progress on the feature.

View file

@ -1,118 +0,0 @@
# C# Language Design Meeting for Feb 8th, 2021
## Agenda
1. Virtual statics in interfaces
1. [Syntax Clashes](#syntax-clashes)
2. [Self-applicability as a constraint](#self-applicability-as-a-constraint)
3. [Relaxed operator operand types](#relaxed-operator-operand-types)
4. [Constructors](#constructor)
## Quote(s) of the Day
- "If you need to kick yourself I see that [redacted] has a foot in the chat you can kick yourself with"
- "Are we at the point where we derail your meeting with other proposals?"
- "It's the classic, noble art of retconning"
## Discussion
Today, we want to take a look at a few top-level design decisions for virtual statics in interfaces that will drive further design and implementation
decisions for this feature.
### Syntax Clashes
C# 8 brought 2 new features to interfaces: default members, and non-virtual static members. This sets up a clash between static virtual members and
static non-virtual members. In pre-C# 8, `interface` members were always virtual and abstract. C# 8 blurred the line here: private and static members
in interfaces are non-virtual. For private members, this makes perfect sense, as the concept of a virtual private method is an oxymoron: if you can't
see it, you can't override it. For statics, it's a bit more unfortunate because we now have inconsistency in the default-virtualness of members in
the interface. We have a few options for addressing this inconsistency:
1. Accept life as it is. We'd require `abstract` and/or `virtual` on static members to mark them as such. There is some benefit here: `static` has
meant something for 21 years in C#, and that is not `virtual`. Requiring a modifier means this does not change.
2. Break the world. Make static members in interfaces mean static virtual by default. This gets us consistency, but breaks consumers in some fashion
and/or introduces multiple dialects of C# to support.
3. We could introduce a bifurcation in interfaces with a `virtual` modifier on the `interface` declaration itself. When marked with `virtual`, `static`
members of the interface are automatically `virtual` by default. This is very not C#-like: we require `static` on all members of `static class`es, why
would `virtual interface`s be any different here? And how would you define the existing non-`virtual` members in such an interface?
Options 2 and 3 also have a question of how they will apply to class members. Due to the size of the changes required we may have to split virtual
static members, shipping with just interfaces in the first iteration and adding class types at a later point. However, we need to make sure that we
design the language side of these changes together, so when class virtual statics are added they don't feel like an afterthought. The second and third
proposals would likely need to have the first proposal for the class version of the feature anyway. While it would be consistent with instance members,
neither of them would totally eliminate the needs to apply the modifiers to interface members.
#### Conclusion
We will require `abstract` or `virtual` be applied to a virtual static member. We will also look at allowing these modifiers for instance interface
methods, even though they are redundant, much like we allow `public` on members today.
### Self-applicability as a constraint
`abstract` static members introduce an interesting scenario in which an interface is no longer valid as a substitute for a type parameter constrained
to that interface type. Consider this scenario:
```cs
interface IHasStatics
{
abstract static int GetValue(); // No implementation of GetValue here
}
class C : IHasStatics
{
static int GetValue() => 0;
}
void UseStatics<T>() where T : IHasStatics
{
int v = T.GetValue();
}
UseStatics<C>(); // C satisfies constraint
UseStatics<IHasStatics>(); // Error: IHasStatics doesn't satisfy constraint
```
The main question here is what do we do about this? We have 2 paths:
1. Forbid interfaces with an abstract static from satisfying a constraint on itself.
2. Forbid access to static virtuals with a type parameter unless you have an additional constraint like `concrete`.
Option 2 seems weird here. Why would a user have constrained to a type that implements an interface, rather than just taking the interface, unless
they wanted to use these methods? Yes, adding an `abstract static` method to an interface where one does not exist today would be a breaking change
for consumers, but that's nothing new: that's why we added DIMs in the first place, and it would continue to be possible to avoid the break by providing
a default method body for the virtual method, instead of making it abstract.
#### Conclusion
We choose option 1.
### Relaxed operator operand types
Today, C# requires that at least one of the operand-types of a user-defined operator be the current type. This breaks down with user-defined virtual
operators in interfaces, however, as the type in the operator won't be the current type, it will be some type derived from the current type. Here,
we naturally look at self types as a possible option. We are concerned with the amount of work that self-types will require, however, and aren't sure
that we want to tie the shipping of virtual statics to the need for self types (and any other associated-type feature). We also need to make sure that
we relax operators enough, and define BCL-native interfaces in a way, such that asymmetric types are representable. For example, a matrix type would
want to be able to add a matrix and a numeric type, and return a matrix. Or `byte`'s `+` operator, which does not return a `byte` today. Given that,
we think it is alright to ship this feature and define a set of operator interfaces without the self type, as we would likely be forced to not use it
in the general interfaces anyway to keep them flexible enough for all our use cases.
#### Conclusion
We're ok with relaxing the constraints as much as we need to here. We won't block on self types being in the language.
### Constructors
Finally, we looked at allowing or disallowing constructors as virtual in interfaces. This is an interesting area: either derived types would be required
to provide a constructor that matches the interface, or derived types would be allowed to not implement interfaces that their base types do. The feature
itself is a parallel to regular static methods; in order to properly describe it in terms of static methods, you'd need to have a self type, which is
what makes it hard to describe in today's C# terms in the first place. Adding this also brings in the question of what do we do about `new()`? This may
be an area where we should prefer a structural approach over a nominal approach, or add a form of type classes to the language: if we were to effectively
deprecate `new()`, that would mean that every type that has a parameterless constructor would need to implement an `IHasConstructor` interface instead.
And we would need to have infinite variations of that interface, and add them to every type in the BCL. This would be a serious issue, both in terms of
sheer surface area required and in terms of effect on type loads and runtime performance penalties for thousands and thousands of new types.
#### Conclusion
We will not have virtual constructors for now. We think that if we add type classes (and allow implementing a type class on a type the user doesn't own),
that will be a better place for them. If we want to improve the `new()` constraint in the mean time, we can look at a more structural form, and users
can work around the lack of additional constraints for now by using a static virtual.

View file

@ -1,168 +0,0 @@
# C# Language Design Meeting for Feb 10th, 2021
## Agenda
1. [Follow up on record equality](#follow-up-on-record-equality)
2. [Namespace directives in top-level programs](#namespace-directives-in-top-level-programs)
3. [Global usings](#global-usings)
4. [Triage](#triage)
1. [Nominal And Collection Deconstruction](#nominal-and-collection-deconstruction)
2. [Sealed record ToString](#sealed-record-ToString)
3. [`using` aliases for tuple syntax](#using-aliases-for-tuple-syntax)
4. [Raw string literals](#raw-string-literals)
5. [Allow `var` variables to be used in a `nameof` in their initializers](#allow-var-variables-to-be-used-in-a-nameof-in-their-initializers)
6. [First-class native integer support](#first-class-native-integer-support)
7. [Extended property patterns](#extended-property-patterns)
## Quote of the Day
- "That's 3 issues in 8 minutes." "That's an LDM record!"
- "Back to serious stuff. Raw string literals. Because raw is better for digestion"
## Discussion
### Follow up on record equality
https://github.com/dotnet/csharplang/issues/39#issuecomment-678097433
https://github.com/dotnet/csharplang/discussions/4411
Back when the linked comment on #39 was raised, we had an internal email chain discussing changing the implementation of `==` to move the
reference equality short-circuit down to the strongly-typed `Equals` implementation. We came to the conclusion that instead of moving the
check we should duplicate it, to ensure that `==` still behaves correctly for `null`, but we never followed up on that conclusion. Today,
we confirmed that we do indeed want to do that, and update the C# compiler to generate this better version of equality.
#### Conclusion
We'll do this. https://github.com/dotnet/roslyn/issues/51136 tracks following up on that work.
### Namespace directives in top-level programs
Our last discussion on the namespace directive had one open question left: should we allow them in combination with top-level statements?
We had an internal email chain on this topic since then, and came to the conclusion that we should disallow this combination. There's a
couple reasons for this:
1. We think the namespace directive is confusing here. It looks like a statement, but the rest of the statements on the top-level don't
introduce a new scope.
2. It is easier to remove an error later if we decide that it's too restrictive. If we allow it, we'd have to support it forever.
3. Users might expect to be able to put a namespace _before_ those top-level statements and change the namespace the implicit `Main` is
generated into.
#### Conclusion
Decision upheld. Top-level statements are not allowed in combination with file-scoped namespace directives.
### Global usings
Our last discussion on global usings had us split right down the middle on whether to make global usings a C# syntax feature, or a
command-line switch to the compiler, with a razor-thin majority leaning to C# syntax. In order to make progress here to begin
implementation, we again took an internal email chain to discuss this further. This conversation drew a few conclusions:
* Neither a a command-line switch nor C# syntax would prevent a source-generator from providing their own set of usings, but the switch
would prevent the source-generator from _dynamically_ providing this, it would have to be predefined in props/targets file in the
generator nuget package.
* We really need to be considering the experience when using `dotnet`, not `csc`. The SDK passes 182 parameters to a build of a simple
hello world application today. It's very unrealistic to base scenarios on calling `csc` directly.
* For the base templates, such as as a console app or a default ASP.NET application, this choice doesn't really affect the files the
templates drop down. In either case, the usings will be hidden. For more complicated templates, this choice changes whether the template
drops a `GlobalUsings.cs` file, or fills in a property in the template csproj. In either case, the template has really moved beyond a
single-file program, so the difference isn't huge.
* We've had a number of requests over the years for global aliases, and a C# syntax will fit in nicely with such a feature.
#### Conclusion
Given the need to start implementing here and the thin majority that prefer C# syntax, we will start investigation and implementation
on the C# syntax version, with a speclet forthcoming later this week.
### Triage
#### Nominal and Collection Deconstruction
https://github.com/dotnet/csharplang/issues/4082
This feature will add symmetry with tuple patterns/deconstruction, which is desirable. There is some potential parsing challenges, as
several of the examples look like a block with a labelled statement inside it and will need some tricky work to ensure disambiguation
works.
##### Conclusion
To the backlog. We like the idea and think there's a way to get it into a form we like, but can't commit to it right now.
#### Sealed record ToString
https://github.com/dotnet/csharplang/issues/4174
This is a simple enough change and we've had a few complaints about the behavior and inability to seal. As an alternative, we could
design a parallel ToString implementation for records that the base type in a hierarchy will call into, which would be a minor behavior
change from C# 9 as released. However, this would be complicated, and `sealed` is the keyword to tell consumers "No really, this is the
final version of this behavior, you can't change it."
##### Conclusion
We'll put this in Any Time to promote community contributions for this small feature, but if no one picks it up we will likely put in a
bit of effort to get it into C# 10.
#### `using` aliases for tuple syntax
https://github.com/dotnet/csharplang/issues/4284
The title of this issue is a bit of a misnomer, as the proposal actually extends to all types, not just tuples. We've also looked at a
broader proposal recently in conjunction with the global using statement feature, which would allow things like
`global using MyDictionary<TValue> = System.Collections.Generic.Dictionary<int, TValue>` and other similar work. It seems like we want
to make some improvements in the alias and using area, and we should consider doing a subset of that larger proposal now in the same
fashion that we've continuously shipped incremental updates for patterns.
##### Conclusion
Triaged into the working set. We want to make a general using improvement push in 10, and have a goal of at least doing this much for
alias improvements in this release.
#### Raw string literals
https://github.com/dotnet/csharplang/issues/4304
There are a number of potentially contentious design decisions in this area, but we intentionally tried to steer away from them today
and limit to just the general concept. There are many scenarios for embedding other languages into C#: XML, JSON/Javascript, C#, and
more. In many of these languages, " and ' are not interchangeable, so anything with a " in it is painful in C# today. We also thing that
interpolation in these strings is important, as these embedded code scenarios are often used for templating. There is potential work around
a way to specify what character sequence defines the interpolation holes, but we did not go into details or specifics here.
##### Conclusion
Into the Working Set. There's a bunch of design questions that we'll need to spend some time working on.
#### Allow `var` variables to be used in a `nameof` in their initializers
https://github.com/dotnet/csharplang/issues/4384
This is a minor convenience issue that has come up a few times for users, but has potential implementation nastiness with disambiguating
whether `nameof` is a method or a `nameof_expression`. Given the minor benefit and the potential implementation concerns, we're concerned
about doing this unless we decide to make `nameof` a non-conditional keyword.
##### Conclusion
Rejected. If we ever make the change to make `nameof` not a conditional keyword and can simplify the binding here, then we can bring this
back, but until that point we will leave this as is.
#### First-class native integer support
https://github.com/dotnet/csharplang/issues/4385
As the language is specified today, compliant C# compilers have to emit certain overflow conversions and then roundtrip those conversions
back to the original representation. This is inefficient and a small spec change will produce no observable semantic difference, while
allowing the compiler to emit better code.
##### Conclusion
Accepted into the working set.
#### Extended property patterns
https://github.com/dotnet/csharplang/issues/4394
This is something that patterns are very verbose about today, and it's a syntax we've previously discussed as a potential shorthand to
reduce the boilerplate. We could also potentially extend this idea to object initializers, but don't want to tie that to this proposal.
##### Conclusion
Accepted into the working set.

View file

@ -1,86 +0,0 @@
# C# Language Design Meeting for Feb 22nd, 2021
## Agenda
1. [Global `using`s](#global-usings)
2. [`using` alias improvements](#using-alias-improvements)
## Quote of the Day
* "You're muted" "I wanted to be muted"
## Discussion
### Global `using`s
https://github.com/dotnet/csharplang/blob/master/proposals/csharp-10.0/GlobalUsingDirective.md
Today we discussed the proposed syntax and restrictions on the current feature specification. As checked in today, the proposal
puts `global` after the `using` directive, which could be potentially ambiguous and require complicated parsing logic, as `global`
is a valid namespace identifier today. For example, this is valid code:
```cs
// Is this a global using alias, or a top-level using variable declaration?
using global myVar = Test.AGlobal;
class Test
{
public static global AGlobal = new global();
}
class global : IDisposable { public void Dispose() {} }
```
We could potentially resolve this in the same way we did for the `record` keyword, which is to forbid types be named `global` in
C# 10. We haven't gotten pushback on this approach for `record` as a type name so far, which is positive. However, aside from the
ambiguity, there are other reasons to view `global`-first as a good thing. `global` here is a modifier on the `using`, which C#
generally puts before the declaration, not after. We also considered 2 separate but related questions:
1. Is `static` a similar modifier? Should it be allowed to come before the `using` as well?
* After discussion, we don't believe so. `static` isn't a modifier on the `using`, it fundamentally changes what the meaning
of the using is about.
2. We already have existing modifiers for C# scopes: should we use `internal` as the keyword here?
* Using `internal` begs the followup question: what does `public` on one of these `using`s mean? What about `private`? We're
uncomfortable with the idea of treating these more like types than like macros. This starts to get into a very slippery slope
of how we export aliases publicly, can additional constraints be added to aliases, and how does that interact with roles?
Finally, we discussed the proposed restrictions on the feature. We like them overall: if `global` and non-`global` `using`s can
be interspersed, it could lead to user confusion as to whether regular `using` directives can affect `global` ones. It also helps
set up a clearly-defined set of scopes. We add a new `global` scope that comes before all top-level `using` statements today, and
their interactions mirror the interactions of regular `using`s statements with those nested in a namespace.
#### Conclusion
We put `global` before `using`, but the proposal is otherwise accepted as is.
### `using` alias improvements
https://github.com/dotnet/csharplang/pull/4452
Continuing with the theme of `using` improvements, we also discussed generalized improvements to `using` aliases to address a
number of reoccuring complaints over the years with limitations in today's approach. Specifically, `using` directives today
cannot reference predefined names like `string`, they can't have generic type arguments, they can't use tuple syntax, or use
pointer types (including the C# 9 function pointer syntax). The current proposal allows all of these things, including aliases
for partially-bound type parameters such as `using MyDictionary<T> = System.Collections.Generic.Dictionary<int, T>;`. When
combined with the previous `global using` feature, enabling this would allow compilation-unit type aliases. Our remaining open
questions here focus again on how we want to push `using`s forward in the future:
1. If we want to push `using` aliases as real types, then they need to have the ability to expression the same things all other
types can, including constraints and variance. If we go this route, we could potentially have a future where you can introduce
an alias that adds a _new_ constraint onto an existing type, such as an invariant `IEnumerable<T>` alias, or a dictionary that
is constrained to only `struct`-type values.
2. If we want to push `using` aliases as more macro-expansion, then the ability to expression constraints and variance is a
confusing detriment. The type parameter will be substituted at expansion site and we'll error at that point if an illegal
substitution is made.
Approach 1 has many followup questions that quickly start to slide into roles territory. Aliases can be used in public API, for
example, so how would we advertise them publicly if the alias has added new constraints? Is it possible for users to introduce
an opaque alias, such as aliasing `int` to `CustomerId` in such a way as there's no implicit conversion between them? Ultimately,
we think that these problems are better solved by a discrete language feature such as roles, rather than as an expansion on
`using` aliases.
#### Conclusion
`using` aliases will be treated more as macro expansions than as real types. We'll check substitutions for concrete types where
we can (such as erroring in the alias itself for `using MyList = System.Collections.Generic.List<int*>;`), but for things we
cannot check at the declaration site, we will error at the use site. There is no way to add constraints to a `using` alias.

View file

@ -1,55 +0,0 @@
# C# Language Design Meeting for Feb 24th, 2021
## Agenda
1. [Static abstract members in interfaces](#static-abstract-members-in-interfaces)
## Quote of the Day
- "I'm not using the monoid word, I'm trying to make it relatable"
## Discussion
### Static abstract members in interfaces
https://github.com/dotnet/csharplang/issues/4436
Today we went over the proposed specification for `static` abstract members. In order to scope the initial implementation, this proposal
intentionally limits such members to _only_ `abstract` members, not `virtual` members. This is due to complexity in passing along the
"instance" that the static is operating on. Consider this example:
```cs
interface I
{
virtual static void I1() { I2(); }
abstract static I2();
}
class C : I
{
public static void I2() {}
}
C.I1();
// How does the runtime pass along the information that the type here is C,
// and I.M1() should invoke C.I2()?
```
Given the complexity of implementation of this scenario and the lack of current motivating examples, we will push this out for a later
version unless our investigations of representing generic math end up needing it.
#### `==` and `!=` operators
These are restricted in interfaces today because interface types cannot implement `object.Equals(object other)` and `object.GetHashcode()`,
which are integral to implementing the operators correctly. We can lift that restriction for `abstract static` members, as the implementing
concrete type will then be required to provide implementations of `Equals(object other)` and `GetHashcode()`.
#### `sealed` on non-abstract members
We don't have any strong feelings on allowing `sealed` on non-virtual `static` interface members. It's fine to include in the proposal.
#### Conversion restrictions
As we implement a set of math interfaces, we'll come across parts of the current restrictions that make it impossible. We should be cautious
here and only lift restrictions as much as is necessary to implement the target scenarios. We can review the rules in depth when they have
been proven out by final usage.

Some files were not shown because too many files have changed in this diff Show more