├── proposals ├── inactive │ └── README.md ├── rejected │ └── README.md ├── csharp-8.0 │ ├── README.md │ ├── null-coalescing-assignment.md │ └── ranges.cs ├── csharp-7.1 │ ├── README.md │ ├── generics-pattern-match.md │ ├── target-typed-default.md │ └── infer-tuple-names.md ├── csharp-7.2 │ ├── leading-separator.md │ └── conditional-ref.md ├── csharp-7.0 │ ├── binary-literals.md │ ├── digit-separators.md │ ├── throw-expression.md │ ├── out-var.md │ └── local-functions.md ├── csharp-7.3 │ ├── improved-overload-candidates.md │ ├── ref-local-reassignment.md │ ├── expression-variables-in-initializers.md │ ├── indexing-movable-fixed-fields.md │ ├── stackalloc-array-initializers.md │ └── auto-prop-field-attrs.md ├── proposal-template.md ├── null-conditional-await.md ├── covariant-returns.md ├── declaration-expressions.md ├── README.md └── fixed-sized-buffers.md ├── .gitattributes ├── .gitignore ├── meetings ├── 2013 │ └── README.md ├── 2015 │ ├── LDM-2015-05-25.md │ └── LDM-2015-08-18.md ├── 2016 │ ├── LDM-2016-09-06.md │ ├── LDM-2016-07-12.md │ ├── LDM-2016-10-18.md │ └── LDM-2016-08-24.md ├── 2017 │ ├── LDM-2017-02-15.md │ ├── LDM-2017-02-14.md │ ├── LDM-2017-03-28.md │ ├── LDM-2017-08-21.md │ ├── LDM-2017-09-25.md │ ├── LDM-2017-01-10.md │ ├── LDM-2017-11-06.md │ ├── LDM-2017-10-09.md │ ├── LDM-2017-10-02.md │ ├── LDM-2017-04-11.md │ ├── LDM-2017-08-16.md │ ├── LDM-2017-05-16.md │ ├── LDM-2017-10-25.md │ ├── LDM-2017-11-29.md │ ├── LDM-2017-01-11.md │ ├── LDM-2017-10-16.md │ ├── LDM-2017-01-17.md │ ├── LDM-2017-06-13.md │ ├── LDM-2017-11-20.md │ ├── LDM-2017-10-18.md │ ├── LDM-2017-12-06.md │ ├── LDM-2017-04-19.md │ ├── LDM-2017-08-28.md │ ├── LDM-2017-11-08.md │ ├── LDM-2017-03-29.md │ ├── LDM-2017-05-26.md │ └── LDM-2017-07-05.md ├── 2018 │ ├── LDM-2018-01-10.md │ ├── LDM-2018-02-05.md │ ├── LDM-2018-08-22.md │ ├── LDM-2018-05-30.md │ ├── LDM-2018-03-28.md │ ├── LDM-2018-04-02.md │ ├── LDM-2018-10-31.md │ ├── LDM-2018-02-28.md │ ├── LDM-2018-02-07.md │ ├── LDM-2018-09-05.md │ ├── LDM-2018-10-10.md │ ├── LDM-2018-05-23.md │ ├── LDM-2018-02-21.md │ ├── LDM-2018-03-19.md │ ├── LDM-2018-10-15.md │ ├── LDM-2018-02-26.md │ ├── LDM-2018-01-31.md │ ├── LDM-2018-03-21.md │ ├── LDM-2018-12-05.md │ └── LDM-2018-12-12.md ├── 2019 │ ├── LDM-2019-01-23.md │ ├── LDM-2019-01-16.md │ ├── LDM-2019-02-25.md │ ├── LDM-2019-01-07.md │ ├── LDM-2019-02-20.md │ └── LDM-2019-01-14.md └── README.md ├── spec └── LICENSE.md ├── Communities.md └── spec_cn └── classes_cn.md /proposals/inactive/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /proposals/rejected/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /proposals/csharp-8.0/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Ignore temporary files 3 | ~$* 4 | *~ 5 | 6 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-02-15.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Feb 15, 2017 2 | 3 | *Upcoming meeting* 4 | 5 | ## Agenda 6 | 7 | - Design Review -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-02-14.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Feb 14, 2017 2 | 3 | *Upcoming meeting* 4 | 5 | ## Agenda 6 | 7 | - Meet with Unity to discuss language features relevant to game developers -------------------------------------------------------------------------------- /spec/LICENSE.md: -------------------------------------------------------------------------------- 1 | THE FOLLOWING NOTICE GOVERNS THE C# SPEC 2 | ===== 3 | 4 | (c) Copyright 1999-2017 Microsoft Corporation. All rights reserved. 5 | Microsoft, Windows, Visual Basic, Visual C#, and Visual C++ are either registered trademarks or trademarks of Microsoft Corporation in the U.S.A. and/or other countries/regions. 6 | Other product and company names mentioned herein may be the trademarks of their respective owners. 7 | -------------------------------------------------------------------------------- /proposals/csharp-7.1/README.md: -------------------------------------------------------------------------------- 1 | 2 | # C# 7.1 3 | 4 | - [Async Main](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/async-main.md) 5 | - [Default Expressions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/target-typed-default.md) 6 | - [Infer tuple names](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/infer-tuple-names.md) 7 | - [Pattern-matching with generics](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/generics-pattern-match.md) 8 | 9 | -------------------------------------------------------------------------------- /meetings/2016/LDM-2016-09-06.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Sep 6, 2016 2 | 3 | ## Agenda 4 | 5 | 1. How do we select `Deconstruct` methods? 6 | 7 | # How do we select Deconstruct methods? 8 | 9 | `(int x, var y) = p` cannot just turn into `p.Deconstruct(out int x, out var y)`, because we want it to find a `Deconstruct` method with a more specific type than `int`, e.g. `byte`. 10 | 11 | We should look only at the arity of the `Deconstruct` method. If there's more than one with the given arity, we fail. If necessary, we will then translate this into passing temporary variables to the `Deconstruct` method, instead of the ones declared in the deconstruction. E.g., if `p` has 12 | 13 | ``` C# 14 | void Deconstruct(out byte x, out byte y) ...; 15 | ``` 16 | 17 | We would translate it equivalently to: 18 | 19 | ``` c# 20 | p.Deconstruct(out byte __x, out byte __y); 21 | (int x, int y) = (__x, __y); 22 | ``` -------------------------------------------------------------------------------- /proposals/csharp-7.2/leading-separator.md: -------------------------------------------------------------------------------- 1 | # Allow digit separator after 0b or 0x 2 | 3 | In C# 7.2, we extend the set of places that digit separators (the underscore character) can appear in integral literals. [Beginning in C# 7.0, separators are permitted between the digits of a literal](../csharp-7.0/digit-separators.md). Now, in C# 7.2, we also permit digit separators before the first significant digit of a binary or hexadecimal literal, after the prefix. 4 | 5 | ```csharp 6 | 123 // permitted in C# 1.0 and later 7 | 1_2_3 // permitted in C# 7.0 and later 8 | 0x1_2_3 // permitted in C# 7.0 and later 9 | 0b101 // binary literals added in C# 7.0 10 | 0b1_0_1 // permitted in C# 7.0 and later 11 | 12 | // in C# 7.2, _ is permitted after the `0x` or `0b` 13 | 0x_1_2 // permitted in C# 7.2 and later 14 | 0b_1_0_1 // permitted in C# 7.2 and later 15 | ``` 16 | 17 | We do not permit a decimal integer literal to have a leading underscore. A token such as `_123` is an identifier. 18 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-01-10.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Jan 10, 2018 2 | 3 | ## Agenda 4 | 5 | 1. Ranges and endpoint types 6 | 7 | 8 | # Ranges and endpoint types 9 | Wouldn't it be nice to have a range type that work on any comparable? Possibly, but we're not eager to solve this right now. 10 | 11 | We need to believe that our future selves can extend the language with some form of target typing, for instance. There's a burden on them (us) to be able to do that without a compat break (silent semantic change), e.g.: There's a type in the future that has conversions so that it would work one way in 7.3 and a different way in the future. 12 | 13 | We probably want `..(int, int)` and `..(long, long)`. We want to treat those as usual operators, so they have to allow user defined conversions of the end points. We could consider blocking off normal conversions of the operands, but that's extremely distasteful. 14 | 15 | So first tentative decision: There's `Range` (for ints) and `LongRange`. The `..` operators are built-in. There's an implicit conversion one way, and an explicit the other, also both built-in. 16 | -------------------------------------------------------------------------------- /proposals/csharp-7.0/binary-literals.md: -------------------------------------------------------------------------------- 1 | # Binary literals 2 | 3 | There’s a relatively common request to add binary literals to C# and VB. For bitmasks (e.g. flag enums) this seems genuinely useful, but it would also be great just for educational purposes. 4 | 5 | Binary literals would look like this: 6 | 7 | ```csharp 8 | int nineteen = 0b10011; 9 | ``` 10 | 11 | Syntactically and semantically they are identical to hexadecimal literals, except for using `b`/`B` instead of `x`/`X`, having only digits `0` and `1` and being interpreted in base 2 instead of 16. 12 | 13 | There’s little cost to implementing these, and little conceptual overhead to users of the language. 14 | 15 | ## Syntax 16 | 17 | The grammar would be as follows: 18 | 19 | ```antlr 20 | integer-literal: 21 | : ... 22 | | binary-integer-literal 23 | ; 24 | binary-integer-literal: 25 | : `0b` binary-digits integer-type-suffix-opt 26 | | `0B` binary-digits integer-type-suffix-opt 27 | ; 28 | binary-digits: 29 | : binary-digit 30 | | binary-digits binary-digit 31 | ; 32 | binary-digit: 33 | : `0` 34 | | `1` 35 | ; 36 | ``` 37 | -------------------------------------------------------------------------------- /meetings/2013/README.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for 2013 2 | 3 | Overview of meetings and agendas for 2013 4 | 5 | ## Oct 7, 2013 6 | 7 | [C# Language Design Notes for Oct 7, 2013](LDM-2013-10-07.md) 8 | 9 | 1. Invariant meaning of names <*scrap the rule*> 10 | 2. Type testing expression <*can’t decide on good syntax*> 11 | 3. Local functions <*not enough scenarios*> 12 | 4. nameof operator <*yes*> 13 | 14 | [C# Language Design Notes for Oct 21, 2013](LDM-2013-10-21.md) 15 | 16 | 1. Primary Constructors <*fleshed out a few more details*> 17 | 2. Lightweight Dynamic <*we examined a much simpler approach*> 18 | 19 | ## Nov 4, 2013 20 | 21 | [C# Language Design Notes for Nov 4, 2013](LDM-2013-11-04.md) 22 | 23 | 1. Initialized and getter-only auto-properties <*details decided*> 24 | 2. Expression-bodied function members <*details decided*> 25 | 3. Lightweight dynamic <*member access model and syntax discussed*> 26 | 27 | ## Dec 16, 2013 28 | 29 | [C# Language Design Notes for Dec 16, 2013](LDM-2013-12-16.md) 30 | 31 | 1. Declaration expressions <*reaffirmed scope rules, clarified variable introduction*> 32 | 2. Semicolon operator <*reaffirmed enclosing parentheses*> 33 | 3. Lightweight dynamic member access <*decided on a syntax*> 34 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-02-05.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Feb 2018 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | Index could be a type 6 | 7 | Then it's about syntax of how to express an Index that's either n from front or n from back. 8 | 9 | How important is "from end"? It's definitely convenient, but its expressiveness is probably not super important. 10 | 11 | Lots of problems with using "-", with overload resolution etc. 12 | 13 | Let's use "^" as a strawman. That takes away some of the ambiguity. 14 | 15 | There's still a question as to how an overloadable "^" would work in the future. It would be a unary operator that returns something different than it takes. How would it be found, if it lives on the result type? There's target typing there, but it may not have a target type, or that may in turn come from overloaded operators. It would have to be handled similarly to conversion in some ways. 16 | 17 | A crazy idea: Use the existing `~` operator! It creates negative numbers, yes, but don't think of them as such. Think of them as integers from end. `~0` is `-1`, `~1` is `-2` etc. This is intriguing, because we'd need *no* extra language support, and just have a convention. 18 | 19 | Quite weird though! -------------------------------------------------------------------------------- /proposals/csharp-7.0/digit-separators.md: -------------------------------------------------------------------------------- 1 | # Digit separators 2 | 3 | Being able to group digits in large numeric literals would have great readability impact and no significant downside. 4 | 5 | Adding binary literals (#215) would increase the likelihood of numeric literals being long, so the two features enhance each other. 6 | 7 | We would follow Java and others, and use an underscore `_` as a digit separator. It would be able to occur everywhere in a numeric literal (except as the first and last character), since different groupings may make sense in different scenarios and especially for different numeric bases: 8 | 9 | ```csharp 10 | int bin = 0b1001_1010_0001_0100; 11 | int hex = 0x1b_a0_44_fe; 12 | int dec = 33_554_432; 13 | int weird = 1_2__3___4____5_____6______7_______8________9; 14 | double real = 1_000.111_1e-1_000; 15 | ``` 16 | 17 | Any sequence of digits may be separated by underscores, possibly more than one underscore between two consecutive digits. They are allowed in decimals as well as exponents, but following the previous rule, they may not appear next to the decimal (`10_.0`), next to the exponent character (`1.1e_1`), or next to the type specifier (`10_f`). When used in binary and hexadecimal literals, they may not appear immediately following the `0x` or `0b`. 18 | 19 | The syntax is straightforward, and the separators have no semantic impact - they are simply ignored. 20 | 21 | This has broad value and is easy to implement. 22 | -------------------------------------------------------------------------------- /proposals/csharp-7.3/improved-overload-candidates.md: -------------------------------------------------------------------------------- 1 | # Improved overload candidates 2 | 3 | ## Summary 4 | [summary]: #summary 5 | 6 | The overload resolution rules have been updated in nearly every C# language update to improve the experience for programmers, making ambiguous invocations select the "obvious" choice. This has to be done carefully to preserve backward compatibility, but since we are usually resolving what would otherwise be error cases, these enhancements usually work out nicely. 7 | 8 | 1. When a method group contains both instance and static members, we discard the instance members if invoked without an instance receiver or context, and discard the static members if invoked with an instance receiver. When there is no receiver, we include only static members in a static context, otherwise both static and instance members. When the receiver is ambiguously an instance or type due to a color-color situation, we include both. A static context, where an implicit this instance receiver cannot be used, includes the body of members where no this is defined, such as static members, as well as places where this cannot be used, such as field initializers and constructor-initializers. 9 | 2. When a method group contains some generic methods whose type arguments do not satisfy their constraints, these members are removed from the candidate set. 10 | 3. For a method group conversion, candidate methods whose return type doesn't match up with the delegate's return type are removed from the set. 11 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-03-28.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Mar 28, 2017 2 | 3 | ## Agenda 4 | 5 | Design some remaining 7.1 features 6 | 7 | 1. Fix pattern matching restriction with generics 8 | 2. Better best common type 9 | 10 | 11 | # Fix pattern matching restriction with generics 12 | 13 | Today `x as T` is allowed if 14 | 15 | 1. there is a reasonable relationship between the left and right type, or 16 | 2. one of them is an open type 17 | 18 | But for the `is` operator we don't have the second. That means that we can't always replace uses of `as` followed by null check with an `is` with a type pattern. 19 | 20 | Also, the restrictions are supposed to rule out only obviously useless cases, whereas some of these are demonstrably useful. 21 | 22 | Let's allow it, look again if there are unexpected problems. 23 | 24 | 25 | # Better best common type 26 | 27 | Best common type should combine a value type with null literal to get a nullable value type. 28 | 29 | ``` c# 30 | b1 ? 1 : b2 ? null : b3 ? 2 : default; // default is 0 31 | b1 ? 1 : b2 ? default : b3 ? 2 : null; // default is null 32 | 33 | b1 ? 1 : (b2 ? null : (b3 ? 2 : default)); // default is 0 34 | b1 ? 1 : (b2 ? default : (b3 ? 2 : null)); // default is null 35 | ``` 36 | 37 | This is weird, but fine: 38 | 39 | ``` c# 40 | M(1, default, null); // int? 41 | ``` 42 | 43 | Next step is trying to spec it. It's a bit complicated. 44 | 45 | ``` c# 46 | M(myShort, myNullableInt, null); Should continue to infer int? , not short? 47 | ``` 48 | -------------------------------------------------------------------------------- /proposals/proposal-template.md: -------------------------------------------------------------------------------- 1 | # FEATURE_NAME 2 | 3 | * [x] Proposed 4 | * [ ] Prototype: [Complete](https://github.com/PROTOTYPE_OWNER/roslyn/BRANCH_NAME) 5 | * [ ] Implementation: [In Progress](https://github.com/dotnet/roslyn/BRANCH_NAME) 6 | * [ ] Specification: [Not Started](pr/1) 7 | 8 | ## Summary 9 | [summary]: #summary 10 | 11 | One paragraph explanation of the feature. 12 | 13 | ## Motivation 14 | [motivation]: #motivation 15 | 16 | Why are we doing this? What use cases does it support? What is the expected outcome? 17 | 18 | ## Detailed design 19 | [design]: #detailed-design 20 | 21 | 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. This section can start out light before the prototyping phase but should get into specifics and corner-cases as the feature is iteratively designed and implemented. 22 | 23 | ## Drawbacks 24 | [drawbacks]: #drawbacks 25 | 26 | Why should we *not* do this? 27 | 28 | ## Alternatives 29 | [alternatives]: #alternatives 30 | 31 | What other designs have been considered? What is the impact of not doing this? 32 | 33 | ## Unresolved questions 34 | [unresolved]: #unresolved-questions 35 | 36 | What parts of the design are still undecided? 37 | 38 | ## Design meetings 39 | 40 | Link to design notes that affect this proposal, and describe in one sentence for each what changes they led to. 41 | 42 | 43 | -------------------------------------------------------------------------------- /proposals/csharp-7.3/ref-local-reassignment.md: -------------------------------------------------------------------------------- 1 | # Ref Local Reassignment 2 | 3 | In C# 7.3, we add support for rebinding the referent of a ref local variable or a ref parameter. 4 | 5 | We add the following to the set of `assignment_operator`s. 6 | 7 | ```antlr 8 | assignment_operator 9 | : '=' 'ref' 10 | ; 11 | ``` 12 | 13 | The `=ref` operator is called the ***ref assignment operator***. It is not a *compound assignment operator*. The left operand must be an expression that binds to a ref local variable, a ref parameter (other than `this`), or an out parameter. The right operand must be an expression that yields an lvalue designating a value of the same type as the left operand. 14 | 15 | The right operand must be definitely assigned at the point of the ref assignment. 16 | 17 | When the left operand binds to an `out` parameter, it is an error if that `out` parameter has not been definitely assigned at the beginning of the ref assignment operator. 18 | 19 | If the left operand is a writeable ref (i.e. it designates anything other than a `ref readonly` local or `in` parameter), then the right operand must be a writeable lvalue. 20 | 21 | The ref assignment operator yields an lvalue of the assigned type. It is writeable if the left operand is writeable (i.e. not `ref readonly` or `in`). 22 | 23 | The safety rules for this operator are: 24 | 25 | - For a ref reassignment `e1 = ref e2`, the *ref-safe-to-escape* of `e2` must be at least as wide a scope as the *ref-safe-to-escape* of `e1`. 26 | 27 | Where *ref-safe-to-escape* is defined in [Safety for ref-like types](../csharp-7.2/span-safety.md) 28 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-08-21.md: -------------------------------------------------------------------------------- 1 | # C# Language Design for Aug 21, 2017 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | 6 | # Type classes 7 | 8 | Multiple implementations (ints and groups) 9 | - You can explicitly provide the type argument for an implicit type parameter 10 | - (but it might get messy) 11 | 12 | 13 | Need explicit instance 14 | - Nothing fundamental preventing a more structural approach 15 | - Two levels of inference possible: 16 | - a: explicit instance, infer members 17 | - b: implicit instance even 18 | 19 | 20 | To bridge to existing interface-based abstractions, you can just provide a very general, generic instance 21 | 22 | 23 | Need an implicit type parameter but don't use it. Maybe a bit too magical. Might be better to require dotting off of the implicit type parameter. For operators that would be nice, though. 24 | 25 | 26 | Instance members? need some syntax like extension methods, maybe, or like explicit interface implementation. 27 | 28 | 29 | Concepts can be for more than one type, so they are not always tied to a single domain type. This may be a step too far, but it does have real value: Graph algorithms that have both Node and Edge types. 30 | 31 | Main competitor, conceptually, would be something that allows for interfaces to play the role of concepts. That comes with challenges of its own, and lots of limitations. But that sort of the thing you have to justify why you're not. 32 | 33 | 34 | Could you use this to make the environment of a lambda a struct? Combined with closures as structs, passed by ref. 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /proposals/csharp-7.0/throw-expression.md: -------------------------------------------------------------------------------- 1 | # Throw expression 2 | 3 | We extend the set of expression forms to include 4 | 5 | ```antlr 6 | throw_expression 7 | : 'throw' null_coalescing_expression 8 | ; 9 | 10 | null_coalescing_expression 11 | : throw_expression 12 | ; 13 | ``` 14 | 15 | The type rules are as follows: 16 | 17 | - A *throw_expression* has no type. 18 | - A *throw_expression* is convertible to every type by an implicit conversion. 19 | 20 | A *throw expression* throws the value produced by evaluating the *null_coalescing_expression*, which must denote a value of the class type `System.Exception`, of a class type that derives from `System.Exception` or of a type parameter type that has `System.Exception` (or a subclass thereof) as its effective base class. If evaluation of the expression produces `null`, a `System.NullReferenceException` is thrown instead. 21 | 22 | The behavior at runtime of the evaluation of a *throw expression* is the same [as specified for a *throw statement*](../../spec/statements.md#the-throw-statement). 23 | 24 | The flow-analysis rules are as follows: 25 | 26 | - For every variable *v*, *v* is definitely assigned before the *null_coalescing_expression* of a *throw_expression* iff it is definitely assigned before the *throw_expression*. 27 | - For every variable *v*, *v* is definitely assigned after *throw_expression*. 28 | 29 | A *throw expression* is permitted in only the following syntactic contexts: 30 | - As the second or third operand of a ternary conditional operator `?:` 31 | - As the second operand of a null coalescing operator `??` 32 | - As the body of an expression-bodied lambda or method. 33 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-09-25.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Sep 25. 2017 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | # Ref readonly locals 6 | 7 | We have nowhere to put a ref readonly result. Ref readonly locals are like ref locals, except that they don't allow mutation of the ref'ed variable. 8 | 9 | ``` 10 | var x = a[1]; 11 | ref readonly var r = ref a[1]; 12 | ``` 13 | 14 | We could have `var` infer the `readonly` as well. It wouldn't be breaking to add later. 15 | 16 | Why do we allow the silent copying of readonly struct values in these new scenarios? Do we like that? 17 | 18 | No, but for consistency. People will need to use analyzers already to catch the existing cases. Those analyzers should just have this feature in there as well. 19 | 20 | We agree that this is a reasonable feature to have, and the design is right. 21 | 22 | Like ref locals, these aren't currently reassignable, but there's no dependence on that. We could change it later. There's then technically room for an extra `readonly` in front. 23 | 24 | For 25 | 26 | ``` c# 27 | MyRefTaker(42); 28 | MyRefTaker(ref MyRefReturner()); 29 | ref readonly int r = 42; 30 | ref readonly int r = ref MyRefReturner(); 31 | b ? 42 : MyRefReturner() 32 | 33 | return ref r; 34 | 35 | ref readonly int x = a[1]; 36 | ``` 37 | 38 | Discussion about whether the implicitness is a good thing. 39 | 40 | There are other options: require "ref" in arguments, require "in" in arguments. We still want the implicit ref in parameters. 41 | 42 | For operators, during overload resolution we ignore the refness. 43 | 44 | ## Conclusion 45 | 46 | Let's flip to requiring `ref` in argument position. `ref 42` and `ref x+y` etc are allowed. 47 | 48 | 49 | (Other small decisions) 50 | 51 | -------------------------------------------------------------------------------- /proposals/csharp-7.3/expression-variables-in-initializers.md: -------------------------------------------------------------------------------- 1 | # Expression variables in initializers 2 | 3 | ## Summary 4 | [summary]: #summary 5 | 6 | We extend the features introduced in C# 7 to permit expressions containing expression variables (out variable declarations and declaration patterns) in field initializers, property initializers, ctor-initializers, and query clauses. 7 | 8 | ## Motivation 9 | [motivation]: #motivation 10 | 11 | This completes a couple of the rough edges left in the C# language due to lack of time. 12 | 13 | ## Detailed design 14 | [design]: #detailed-design 15 | 16 | We remove the restriction preventing the declaration of expression variables (out variable declarations and declaration patterns) in a ctor-initializer. Such a declared variable is in scope throughout the body of the constructor. 17 | 18 | We remove the restriction preventing the declaration of expression variables (out variable declarations and declaration patterns) in a field or property initializer. Such a declared variable is in scope throughout the initializing expression. 19 | 20 | We remove the restriction preventing the declaration of expression variables (out variable declarations and declaration patterns) in a query expression clause that is translated into the body of a lambda. Such a declared variable is in scope throughout that expression of the query clause. 21 | 22 | ## Drawbacks 23 | [drawbacks]: #drawbacks 24 | 25 | None. 26 | 27 | ## Alternatives 28 | [alternatives]: #alternatives 29 | 30 | The appropriate scope for expression variables declared in these contexts is not obvious, and deserves further LDM discussion. 31 | 32 | ## Unresolved questions 33 | [unresolved]: #unresolved-questions 34 | 35 | - [ ] What is the appropriate scope for these variables? 36 | 37 | ## Design meetings 38 | 39 | None. 40 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-08-22.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for August 22, 2018 2 | 3 | # Agenda 4 | 5 | 1. Target-typed new 6 | 1. Clarification on constraints with nullable reference types enabled 7 | 8 | # Discussion 9 | 10 | ## Target-typed new 11 | 12 | Proposal: https://github.com/dotnet/csharplang/blob/master/proposals/target-typed-new.md 13 | 14 | *Question: Should target-typed `new` be allowed for tuple types?* 15 | 16 | We currently don't allow the constructor syntax `new (int, int)(0, 0)`. 17 | Should we allow `(int, int) t = new(0, 0)`? Would this mean the same thing as 18 | a tuple literal, or a call to a constructor on System.ValueTuple? This would 19 | also expose some of the differences between ValueTuple and tuple types, in 20 | that there is no constructor for a tuple type with greater than 7 elements. 21 | 22 | Decision: Let's allow it, as long as that doesn't require a lot of extra 23 | work. The meaning would be to call the underlying System.ValueTuple 24 | constructors. This would expose differences in tuples with a lot of elements, 25 | but this seems like a very rare and unimportant case. 26 | 27 | *Question: Allow `throw new()`? It would convert to bare `Exception` by the spec.* 28 | 29 | Decision: Disallow. Fundamentally, we don't like this stylistically. 30 | 31 | *Question: Allow `new()` with user-defined comparison and arithmetic operators?* 32 | 33 | Decision: Allow. 34 | 35 | 36 | ## Generic constraints with nullable reference types 37 | 38 | *Question: In the following snippet, is `U` a non-nullable reference type?* 39 | 40 | ```C# 41 | void M() where T : class, U : T` {} 42 | ``` 43 | 44 | Answer: Yes 45 | 46 | *Question: In the following snippet, is `I` a non-nullable reference type?* 47 | 48 | ```C# 49 | interface I {} 50 | void M() where T : I {} 51 | ``` 52 | 53 | Answer: Yes 54 | 55 | *Q: Do we want to warn for redundant constraints?* 56 | 57 | A: We don't currently. Let's stay with that decision for now. -------------------------------------------------------------------------------- /proposals/csharp-7.0/out-var.md: -------------------------------------------------------------------------------- 1 | # Out variable declarations 2 | 3 | The *out variable declaration* feature enables a variable to be declared at the location that it is being passed as an `out` argument. 4 | 5 | ```antlr 6 | argument_value 7 | : 'out' type identifier 8 | | ... 9 | ; 10 | ``` 11 | 12 | A variable declared this way is called an *out variable*. You may use the contextual keyword `var` for the variable's type. The scope will be the same as for a *pattern-variable* introduced via pattern-matching. 13 | 14 | According to Language Specification (section 7.6.7 Element access) the argument-list of an element-access (indexing expression) does not contain ref or out arguments. However, they are permitted by the compiler for various scenarios, for example indexers declared in metadata that accept `out`. 15 | 16 | Within the scope of a local variable introduced by an argument_value, it is a compile-time error to refer to that local variable in a textual position that precedes its declaration. 17 | 18 | It is also an error to reference an implicitly-typed (§8.5.1) out variable in the same argument list that immediately contains its declaration. 19 | 20 | Overload resolution is modified as follows: 21 | 22 | We add a new conversion: 23 | 24 | > There is a *conversion from expression* from an implicitly-typed out variable declaration to every type. 25 | 26 | Also 27 | 28 | > The type of an explicitly-typed out variable argument is the declared type. 29 | 30 | and 31 | 32 | > An implicitly-typed out variable argument has no type. 33 | 34 | The *conversion from expression* from an implicitly-typed out variable declaration is not considered better than any other *conversion from expression*. 35 | 36 | The type of an implicitly-typed out variable is the type of the corresponding parameter in the signature of the method selected by overload resolution. 37 | 38 | The new syntax node `DeclarationExpressionSyntax` is added to represent the declaration in an out var argument. 39 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-01-10.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Jan 10, 2017 2 | 3 | ## Agenda 4 | 5 | - Discriminated unions via "closed" types 6 | 7 | # Discriminated unions via "closed" types 8 | 9 | There's a [proposal](https://github.com/dotnet/roslyn/issues/8729) to allow adding `closed` to an abstract type. This prevents inheriting from any other assembly, meaning that there would be a known set of derived types, all in the same assembly as the closed abstract type. This is somewhat similar to Scala's case classes. 10 | 11 | At the metadata level, this could possibly be implemented by generating an internal abstract member. In fact that member could make itself useful as a property returning a tag, so we can do efficient tag-based switching. 12 | 13 | A nice aspect is that `closed` can be added to existing hierarchies, adding a notion of completeness and protecting from "unauthorized" inheritance. 14 | 15 | Adding this to existing code may lead to completeness warnings in consuming code. 16 | 17 | In a lot of places where you switch, you don't even *want* completeness. It's almost like it's something you have to ask for at the switch site. Special syntax? 18 | 19 | "Catch all" is a problem. Often I want to have a catch all *even* as I want to be told if there's a new case. We *could* say that if there's a closed type, then you need to be complete in a switch. Which you can be with a `default`, but then you won't know if there's a new case: it's up to you. 20 | 21 | Enums: We could allow `closed` on those, and then eliminate the explicit conversion to it, as well as arithmetic. Switching could generate a default that throws, in case someone monkeyed an illegal value in there. 22 | 23 | A closed enum wouldn't get much value out of being an enum, since we don't want operators to work on them. We could consider making them not be enums from the runtime perspective. 24 | 25 | ## Conclusion 26 | 27 | An interesting approach to discriminated unions that might be a better fit with the spirit of C#. 28 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-05-30.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for May 30, 2018 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | # Annotating parts of a project 6 | 7 | Roslyn starts out with about 2000 warnings. We may want to support nullable annotations for only a part of a project, so that you can gradually ease into them. 8 | 9 | This touches on a core question: Are there separate opt-ins for *consuming* nullability (give me warnings please) and *producing* nullability (consider unannotated reference types to be non-nullable; "URTANN"). 10 | 11 | If we have a scoped "URTANN", then turning on warnings for the whole file would still have limited impact, until annotations start becoming abundant. 12 | 13 | But we may also want to consider warnings to be turned on in a scoped way, at a certain granularity. Source files? Fine grained program elements? It might be better not having this, though, as it comes with the risk of introducing more problems (through annotation), without discovering it (because the consumption has warnings off). 14 | 15 | ``` c# 16 | T M(string s) => M2(s); // No warning because s is oblivious 17 | 18 | [URTANN] 19 | T M2(string s) => ... 20 | ``` 21 | 22 | The opt-in to warnings could be a "fake" error code that represents a category. We may not need a brand new dedicated command line option, but we're willing to go there. 23 | 24 | 1. We agree that we are comfortable (for now at least) in opting in to the warnings at the whole project level only 25 | 2. We agree that there should be a separate mechanism for opting in/out of non-null annotations (URTANN) 26 | 3. We agree that URTANN should be attribute-based, and able to be applied (or unapplied, with a `false` argument) at multiple levels of program elements 27 | 28 | Decision: 29 | 30 | Let's have `URTANN(true)` implicit by default, unless you have another module-level URTANN. We can maybe auto-generate it if you don't have it already. It should be called `NonNullTypesAttribute(bool)`. -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-03-28.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Mar 28, 2018 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | Ranges 6 | 7 | # Shipping a prototype 8 | 9 | We would like to ship a prototype of ranges, in order to get feedback and settle the remaining decisions. 10 | 11 | We would need to offer the Range type, maybe as a NuGet. 12 | 13 | But it would also be nice to offer range-enhanced other types, like `Span` and maybe arrays. 14 | 15 | Maybe the compiler can fake out special indexers in the prototype. Or we also add extension indexers. 16 | 17 | Tying it to revs of .NET Core previews may be too sluggish, and fraught with dependencies. 18 | 19 | Or we create a wrapper type for Spans with the extra behavior, and smooth over the edges as best we can (implicit conversions etc) 20 | 21 | ## Conclusion 22 | 23 | Let's do: 24 | 25 | 1. A temporary "language feature" that's extension indexers based on method names 26 | 2. Range support in the language 27 | 3. A preview NuGet package with `Range`, associated types and "extension indexers" on known types 28 | 29 | 30 | # Which feature? 31 | 32 | We have two options: 33 | 34 | 1. Do like Python: just live with "0 from end" not being expressible as a number; it's a special case 35 | 2. Add Index and ^ 36 | 37 | The first one is the more restrictive, and also the cheaper one (from both work and number of abstractions). This will help us understand whether people need the extra. 38 | 39 | But not supporting "from end" is too restrictive; we have very good reason to believe people need that. 40 | 41 | 42 | # Nested stackalloc 43 | 44 | Issue #1412, trivial to implement in 8.0, where the stack-spilling machinery is there anyway for pattern matching. 45 | 46 | 47 | # Exhaustiveness in switch expressions 48 | 49 | Should non-exhaustiveness be an error or a warning? If warning, what should happen at runtime? 50 | 51 | For now, it's a warning, and if you get there we throw a new exception type for this. 52 | 53 | -------------------------------------------------------------------------------- /proposals/null-conditional-await.md: -------------------------------------------------------------------------------- 1 | # null-conditional await 2 | 3 | * [x] Proposed 4 | * [ ] Prototype: None 5 | * [ ] Implementation: None 6 | * [ ] Specification: Started, below 7 | 8 | ## Summary 9 | [summary]: #summary 10 | 11 | Support an expression of the form `await? e`, which awaits `e` if it is non-null, otherwise it results in `null`. 12 | 13 | ## Motivation 14 | [motivation]: #motivation 15 | 16 | This is a common coding pattern, and this feature would have nice synergy with the existing null-propagating and null-coalescing operators. 17 | 18 | ## Detailed design 19 | [design]: #detailed-design 20 | 21 | We add a new form of the *await_expression*: 22 | 23 | ```antlr 24 | await_expression 25 | : 'await' '?' unary_expression 26 | ; 27 | ``` 28 | 29 | The null-conditional `await` operator awaits its operand only if that operand is non-null. Otherwise the result of applying the operator is null. 30 | 31 | The type of the result is computed using the [rules for the null-conditional operator](https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#null-conditional-operator). 32 | 33 | > **NOTE:** 34 | > If `e` is of type `Task`, then `await? e;` would do nothing if `e` is `null`, and await `e` if it is not `null`. 35 | > 36 | > If `e` is of type `Task` where `K` is a value type, then `await? e` would yield a value of type `K?`. 37 | 38 | ## Drawbacks 39 | [drawbacks]: #drawbacks 40 | 41 | As with any language feature, we must question whether the additional complexity to the language is repaid in the additional clarity offered to the body of C# programs that would benefit from the feature. 42 | 43 | ## Alternatives 44 | [alternatives]: #alternatives 45 | 46 | Although it requires some boilerplate code, uses of this operator can often be replaced by an expression something like `(e == null) ? null : await e` or a statement like `if (e != null) await e`. 47 | 48 | ## Unresolved questions 49 | [unresolved]: #unresolved-questions 50 | 51 | - [ ] Requires LDM review 52 | 53 | ## Design meetings 54 | 55 | None. 56 | -------------------------------------------------------------------------------- /proposals/csharp-7.3/indexing-movable-fixed-fields.md: -------------------------------------------------------------------------------- 1 | # Indexing `fixed` fields should not require pinning regardless of the movable/unmovable context. # 2 | 3 | The change has the size of a bug fix. It can be in 7.3 and does not conflict with whatever direction we take further. 4 | This change is only about allowing the following scenario to work even though `s` is moveable. It is already valid when `s` is not moveable. 5 | 6 | NOTE: in either case, it still requires `unsafe` context. It is possible to read uninitialized data or even out of range. That is not changing. 7 | 8 | ```csharp 9 | unsafe struct S 10 | { 11 | public fixed int myFixedField[10]; 12 | } 13 | 14 | class Program 15 | { 16 | static S s; 17 | 18 | unsafe static void Main() 19 | { 20 | int p = s.myFixedField[5]; // indexing fixed-size array fields would be ok 21 | } 22 | } 23 | ``` 24 | 25 | The main “challenge” that I see here is how to explain the relaxation in the spec. 26 | In particular, since the following would still need pinning. 27 | (because `s` is moveable and we explicitly use the field as a pointer) 28 | 29 | ```csharp 30 | unsafe struct S 31 | { 32 | public fixed int myFixedField[10]; 33 | } 34 | 35 | class Program 36 | { 37 | static S s; 38 | 39 | unsafe static void Main() 40 | { 41 | int* ptr = s.myFixedField; // taking a pointer explicitly still requires pinning. 42 | int p = ptr[5]; 43 | } 44 | } 45 | ``` 46 | 47 | One reason why we require pinning of the target when it is movable is the artifact of our code generation strategy, - we always convert to an unmanaged pointer and thus force the user to pin via `fixed` statement. However, conversion to unmanaged is unnecessary when doing indexing. The same unsafe pointer math is equally applicable when we have the receiver in the form of a managed pointer. If we do that, then the intermediate ref is managed (GC-tracked) and the pinning is unnecessary. 48 | 49 | The change https://github.com/dotnet/roslyn/pull/24966 is a prototype PR that relaxes this requirement. 50 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-11-06.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Nov 6, 2017 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | # Roslyn 20870 6 | 7 | Protecting the client from unintended dependencies. But also protects from servicing. Today people going through reflection *know* they're being bad. Would this give enough sense that they are doing something special. 8 | 9 | It would make consumers lazy about contacting the API owner about things they need exposed. 10 | 11 | It would be an arms race - we would want the `IgnoreIgnore...` attribute to *really* protect things. 12 | 13 | People will still have expectations about dependencies even if it was "their own fault" by using this attribute. 14 | 15 | ## Conclusion 16 | Too risky/fishy in too many ways. 17 | 18 | 19 | # Roslyn 17310 20 | 21 | There is no good language level solution right now. This is better addressed with an analyzer, which can know specifically about SpinLock (for instance). 22 | 23 | In time, when readonly struct declarations are added, as well as maybe the ability to declare individual struct members as readonly, *then* maybe we could start warn. 24 | 25 | ## Conclusion 26 | 27 | Not at the language level 28 | 29 | 30 | # Roslyn 20450 31 | 32 | We have sympathy. It feels like a corner that's cut. But it's quite expensive to implement, and has semantic dark corners (`List<>.First.Foo`). 33 | 34 | ## Conclusion 35 | 36 | Not now. 37 | 38 | 39 | # Roslyn 20015 40 | 41 | When default-expressions are constant (according to the language) this is not interesting expressiveness - there's a literal you can use. 42 | 43 | When they are *not* it gets a bit more interesting - you might want to check that your custom struct is zero-initialized. But you can do that with equality. Even in recursive scenarios, you can just `var`-pattern it and check in `when` or `&&`. 44 | 45 | Additionally there is some concern about the target type being clear enough for the `default` expression. 46 | 47 | ## Conclusion 48 | 49 | No. 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-04-02.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Review Apr 2, 2018 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | 6 | # C# 8.0 7 | 8 | Tag with needs runtime support or ecosystem support 9 | Process: make it more clear where we are. Help people understand when beating on a feature would be wasting their time. 10 | 11 | # Nullable 12 | 13 | Make sure we work backwards to understand how long it takes to build the whole experience. 14 | 15 | ## Dotted names 16 | 17 | We probably have a good level of invalidation. 18 | 19 | ## Type strengthening 20 | 21 | Based on null state and `!`. Should definitely keep that. 22 | 23 | ## ! 24 | 25 | Because `!` only applies "right here", it is ok to also silence warnings recursively. But we should not consider automatically flowing `!` on the given execution path then, because you may not always want to silence all warnings on the variable subsequently. 26 | 27 | ## Unannotated assemblies 28 | 29 | Maybe there should be a warning that you are referencing unannotated assemblies. 30 | 31 | ## Tracking non-null variables and "W" warnings 32 | 33 | Understand the motivation. This is ok. 34 | 35 | ## Type parameters 36 | 37 | Unconstrained may be either nullable or nonnullable, so we have to be defensive. That's quite restrictive, but probably right. 38 | 39 | ## Structural relationships 40 | 41 | In TypeScript there are more type relationships because of structural types. We don't even get to first base here. 42 | 43 | # Ranges 44 | 45 | ## Open 46 | 47 | Sure about syntax? Should there be `*` instead? 48 | 49 | ## From end 50 | 51 | Indexing from end is probably more common in Python than any ranges at all! Cutting that off with `-x` syntax is a shame. 52 | 53 | If we weren't doing `^`, just do ranges with positive numbers. Solve the "from end" problem in general or not at all. 54 | 55 | ## Conclusion 56 | 57 | If indexing and multiple dimensions are in the core syntax, might as well do the whole enchilada. Optimize in compiler when using `^` on arrays and strings. 58 | 59 | 60 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-10-31.md: -------------------------------------------------------------------------------- 1 | 2 | # C# Design Review Notes for Oct 31, 2018 3 | 4 | This was a review with the full design team (including Anders) to see how the 5 | whole release is shaping out. 6 | 7 | ## Discussion 8 | 9 | ### Nullable 10 | 11 | #### Flow analysis to turn a non-nullable type to nullable 12 | 13 | The question is whether flow analysis can cause types to become nullable if a 14 | value of non-nullable type is compared to null. 15 | 16 | ```C# 17 | void M(string x) 18 | { 19 | if (x == null) 20 | { 21 | // is x now treated as ‘string?’ here? 22 | } 23 | } 24 | ``` 25 | 26 | This is an issue that TypeScript has dealt with. There's some worry that most 27 | of the warnings will be produced not at the place with the problem. We should 28 | be careful that we're not going to annoy the user. 29 | 30 | #### Flow analysis and refactoring 31 | 32 | Flow analysis constrains refactoring because something may be tested null by 33 | flow analysis, but if you pass to a new method, the flow analysis is lost. For 34 | example: 35 | 36 | ```C# 37 | class C 38 | { 39 | string? Prop1 { get; } 40 | string? Prop2 { get; } 41 | } 42 | 43 | class C2 44 | { 45 | void M1(C c) 46 | { 47 | if (c.Prop1 != null && c.Prop2 != null) 48 | { 49 | M2(c); 50 | } 51 | } 52 | 53 | void M2(C c) 54 | { 55 | // The null checking from M1 is lost here and M2 has to 56 | // check again for null to avoid a warning 57 | c.Prop1.Equals(...) 58 | } 59 | } 60 | ``` 61 | 62 | #### `!` on parameters 63 | 64 | Very different behavior depending on where `!` appears -- maybe too many 65 | meanings. 66 | 67 | #### Treatment of lambdas 68 | 69 | For 70 | 71 | ```C# 72 | void M() 73 | { 74 | int? x = null; 75 | Action a = () => x = 0; 76 | a(); 77 | // What's the null state of `x` here? 78 | } 79 | ``` 80 | 81 | Treating the delegate conversion as executing the method is unsafe, but 82 | not doing so is conservative and will warn on valid checking. TypeScript 83 | has also hit this and there's no easy answer. -------------------------------------------------------------------------------- /proposals/csharp-7.3/stackalloc-array-initializers.md: -------------------------------------------------------------------------------- 1 | # Stackalloc array initializers 2 | 3 | ## Summary 4 | [summary]: #summary 5 | 6 | Allow array initializer syntax to be used with `stackalloc`. 7 | 8 | ## Motivation 9 | [motivation]: #motivation 10 | 11 | Ordinary arrays can have their elements initialized at creation time. It seems reasonable to allow that in `stackalloc` case. 12 | 13 | The question of why such syntax is not allowed with `stackalloc` arises fairly frequently. 14 | See, for example, [#1112](https://github.com/dotnet/csharplang/issues/1112) 15 | 16 | ## Detailed design 17 | 18 | Ordinary arrays can be created through the following syntax: 19 | 20 | ```csharp 21 | new int[3] 22 | new int[3] { 1, 2, 3 } 23 | new int[] { 1, 2, 3 } 24 | new[] { 1, 2, 3 } 25 | ``` 26 | 27 | We should allow stack allocated arrays be created through: 28 | 29 | ```csharp 30 | stackalloc int[3] // currently allowed 31 | stackalloc int[3] { 1, 2, 3 } 32 | stackalloc int[] { 1, 2, 3 } 33 | stackalloc[] { 1, 2, 3 } 34 | ``` 35 | 36 | The semantics of all cases is roughly the same as with arrays. 37 | For example: in the last case the element type is inferred from the initializer and must be an "unmanaged" type. 38 | 39 | NOTE: the feature is not dependent on the target being a `Span`. It is just as applicable in `T*` case, so it does not seem reasonable to predicate it on `Span` case. 40 | 41 | ## Translation 42 | 43 | The naive implementation could just initialize the array right after creation through a series of element-wise assignments. 44 | 45 | Similarly to the case with arrays, it might be possible and desirable to detect cases where all or most of the elements are blittable types and use more efficient techniques by copying over the pre-created state of all the constant elements. 46 | 47 | ## Drawbacks 48 | [drawbacks]: #drawbacks 49 | 50 | ## Alternatives 51 | [alternatives]: #alternatives 52 | 53 | This is a convenience feature. It is possible to just do nothing. 54 | 55 | ## Unresolved questions 56 | [unresolved]: #unresolved-questions 57 | 58 | ## Design meetings 59 | 60 | None yet. 61 | -------------------------------------------------------------------------------- /meetings/2019/LDM-2019-01-23.md: -------------------------------------------------------------------------------- 1 | 2 | # C# Language Design Meeting for Jan 23, 2019 3 | 4 | ## Agenda 5 | 6 | Function pointers ([Updated proposal](https://github.com/dotnet/csharplang/blob/master/proposals/function-pointers.md)) 7 | 8 | ## Discussion 9 | 10 | ### Creation of a function pointer to a managed method 11 | 12 | The proposal is `&Class.Method` to produce a function pointer. The question 13 | is whether `&Class.Method` is target-typed, whether it has a natural type 14 | when there's only one member in the method group, or both. 15 | 16 | Target-typing is useful because, like with delegates, it allows you to select 17 | a unique method out of a method group with multiple incompatible overloads. 18 | 19 | Natural type is useful because it allows things like `var` and `void*`. 20 | 21 | **Conclusion** 22 | 23 | Let's start by only doing target-typing. Also, the section "better function 24 | member" is not necessary without the natural typing. 25 | 26 | ### DllImport CallingConvention? 27 | 28 | There is actually a stub that the compiler calls for P/Invoke with DllImport 29 | that is always done using the managed calling convention, so there's no 30 | reason for function pointers to use the DllImportAttribute. 31 | 32 | NativeCallback is intended for the scenario where you want to avoid the stub 33 | overhead. 34 | 35 | ### NativeCallableAttribute 36 | 37 | Let's look at this in more detail. 38 | 39 | ### Syntax 40 | 41 | ```C# 42 | 1. func* managed int(string) 43 | 2. func*(string)->int 44 | 3. func* managed (string)->int 45 | 4. func* managed (string)=>int 46 | 5. managed int(string)* 47 | 5a. int(string)* 48 | 6. managed int(string) 49 | 7. managed (string)->int 50 | 8. delegate* int(string) 51 | 9. func int(string)* 52 | 10. delegate int(string)* 53 | ``` 54 | 55 | **Conclusion** 56 | 57 | We're not sure about all the potential ambiguities here. Let's look at (5a), 58 | possibly disambiguating with the calling convention. 59 | 60 | ### Things to clarify in spec 61 | 62 | * What does the CLR do if you try to call a method that has a modreq/modopt in 63 | the signature, but the `calli` has the signature without the modreq/modopt? -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-10-09.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Oct 9, 2017 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | 6 | # 882 Negated if or negative patterns 7 | 8 | Three approaches 9 | 10 | 1. bang outside if condition (then should I do that on while etc, too) `if !(o is int i)` 11 | 2. negative patterns (but not very useful recursively) `not int i` 12 | 3. `is not` as an expression operator 13 | 14 | # 867 15 | 16 | Avoid some statement cliffs... 17 | 18 | Put it in X.X with a note to consider again when we have match expressions 19 | 20 | # 414 21 | 22 | There's a "there" there. 23 | 24 | We think this should be addressed, and will keep the championing issue to represent it. 25 | 26 | However, it should be different: 27 | 28 | 1. It should not be strongly tied to the `Dictionary` type, but be target typed 29 | 2. We should look at initializing immutable objects (also for object and collection initializers) 30 | 3. We already have index initializers. Are they good enough? 31 | 32 | # 973 Declaration expressions 33 | 34 | Last time, we had two issues: 35 | 36 | 1. Weren't ready to commit to scoping rules 37 | 2. Weren't sure that we could get decent error recovery on syntax 38 | 39 | 1 is dealt with. 40 | 2 was more that it was hard to show intellisense because more things were legal 41 | 42 | Scenario is introduce a local variable in expressions without having to use trivial pattern matching. Also ref. 43 | 44 | We feel like we need to spend more time with it to judge its value. 8.0 for now to trigger that discussion. 45 | 46 | # 881 and 33 47 | 48 | Fits with nullable in 8.0 49 | 50 | # 185 51 | 52 | Settle this in the 7.3 timeframe 53 | 54 | # 187 Blittable 55 | 56 | # 435 57 | 58 | # 287 59 | 60 | # 32 61 | 62 | # 125 63 | 64 | Missing, but not much ask for it 65 | 66 | # 111 67 | 68 | We would want to deal with single parameters. A problem is that discards do not shadow today, whereas identifiers do. We may want to change that. 69 | 70 | # 191 71 | 72 | Need more motivation 73 | 74 | # 190 75 | 76 | Some open design discussions 77 | 78 | # 79 | 80 | 81 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-10-02.md: -------------------------------------------------------------------------------- 1 | # Milestone philosophy 2 | 3 | 4 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 5 | 6 | 7.3 is a bucket for next steps with pattern matching. 7 | 8 | Non-exhaustive list 9 | 10 | - recursive patterns 11 | - non-null patterns 12 | - switch expression 13 | - negated if-condition 14 | 15 | 8.0 is for major language features 16 | 17 | 18 | # Discussion on how we get input 19 | 20 | We should solicit problems, not just solutions 21 | 22 | # Triage 23 | 24 | ## 945 25 | Could make it always prefer by-value as a tie-breaker. 26 | 27 | ## 933 28 | Motivating scenarios: 29 | 30 | 1. Hold on to the content variable in linked list elements 31 | 2. assign one as a default and have an if overwrite it with another 32 | 33 | Syntax! Should we be putting `ref` in front of the LHS, or just the RHS? 34 | 35 | ``` c# 36 | ref r = ref v; // or 37 | r = ref v; 38 | ``` 39 | 40 | Not requiring ref lets us: 41 | 42 | - Be more terse 43 | - Work as an expression (because no expression starts with ref) 44 | 45 | Requiring makes it syntactically clear whether you are assigning to `r` itself (in the ref space) or to the variable currently pointed to by `r` (in the value space). Also, what the hell does code mean if e.g. a ref-reassigning expression occurs as a ref or out argument? 46 | 47 | ``` c# 48 | M(out r = ref v); //What? 49 | ``` 50 | 51 | We'd just recommend parenthesizing the assignment, like we recommend everywhere else assignments are used as expressions. 52 | 53 | There's a limit which is that there's no proper default, so we'd still always require initialization, picking up the lifetime from the initializer. This is a bit painful when you want it to have global lifetime (no good default to provide). 54 | 55 | We should instead allow you to not have an initializer. We do definite assignment analysis. It has global lifetime. 56 | 57 | 58 | 59 | ``` c# 60 | ref readonly tmp = ref Get(); 61 | M(in tmp); 62 | ``` 63 | 64 | Annoying that there's sort of three different ways to talk about a `ref readonly`: `ref readonly`, `ref` and `in`. 65 | 66 | Should we switch parameter to `ref readonly`? Allow choice. 67 | 68 | No: Let's keep having only one way of doing it, and let's have that way be consistent with what you say at the call site. 69 | 70 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-02-28.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Feb 28, 2018 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | Parameters: Value parameters can be treated like locals, but we may not want to. Ref and out parameters need to be guarded by non-W warnings. 6 | 7 | Is it a problem that this can lead to two related warnings? Maybe a little bit, but it's actually mostly good. You can address the declaration of s2 in three different ways: 8 | 9 | * Make ns non-null before assigning 10 | * Put a question mark on s2 11 | * Turn of W warnings 12 | Would be weird if that last one introduced another warning! 13 | 14 | Type inference: 15 | 16 | We could have `var` always have `?`. 17 | 18 | ``` c# 19 | static T[] MakeStack(T element) 20 | { 21 | 22 | } 23 | static int GetLengthOfMiddleName(Person p) 24 | { 25 | string? middleName = p.MiddleName; 26 | 27 | //return middleName.Length; 28 | if (middleName is null) return 0; 29 | var stack = MakeStack(middleName); // infer string[] or string?[] 30 | } 31 | 32 | 33 | bool b = false; 34 | string s = null; // suppressible 35 | var s2 = 36 | //(b ? s : s); 37 | Choose(b, s, s); 38 | var l = s2.Length; // not suppressible 39 | 40 | ``` 41 | 42 | Should we have type inference depend on the declared or the flowed type state? We reiterated that discussion, but conclude (again) that it's the null state that counts. This maximally helps avoid unnecessary warnings on legacy code. 43 | 44 | ## Cast and non-null 45 | 46 | ``` c# 47 | var s1 = "Hello"; // But I want to assign null later 48 | var s2 = (string?)"Hello"; // Either disallowed, makes no difference or forgets null state 49 | var? s3 = "Hello"; // Nullable but keeps the null state 50 | ``` 51 | 52 | So `var?` would be more useful than casts for `var` scenarios (because you are declaring a variable whose null state matters later). For generic arguments it doesn't matter to keep the flow state, so `(string?)` cast works fine. 53 | 54 | It's a little weird that `s2` and `s3` don't work quite the same way. They do today. 55 | 56 | Still, as a plan of record let's do this. 57 | 58 | So we keep the casts, and tentatively keep `var?`. 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /proposals/csharp-7.1/generics-pattern-match.md: -------------------------------------------------------------------------------- 1 | # pattern-matching with generics 2 | 3 | * [x] Proposed 4 | * [ ] Prototype: 5 | * [ ] Implementation: 6 | * [ ] Specification: 7 | 8 | ## Summary 9 | [summary]: #summary 10 | 11 | The specification for the [existing C# as operator](../../spec/expressions.md#the-as-operator) permits there to be no conversion between the type of the operand and the specified type when either is an open type. However, in C# 7 the `Type identifier` pattern requires there be a conversion between the type of the input and the given type. 12 | 13 | We propose to relax this and change `expression is Type identifier`, in addition to being permitted in the conditions when it is permitted in C# 7, to also be permitted when `expression as Type` would be allowed. Specifically, the new cases are cases where the type of the expression or the specified type is an open type. 14 | 15 | ## Motivation 16 | [motivation]: #motivation 17 | 18 | Cases where pattern-matching should "obviously" be permitted currently fail to compile. See, for example, https://github.com/dotnet/roslyn/issues/16195. 19 | 20 | ## Detailed design 21 | [design]: #detailed-design 22 | 23 | We change the paragraph in the pattern-matching specification (the proposed addition is shown in bold): 24 | 25 | > Certain combinations of static type of the left-hand-side and the given type are considered incompatible and result in compile-time error. A value of static type `E` is said to be *pattern compatible* with the type `T` if there exists an identity conversion, an implicit reference conversion, a boxing conversion, an explicit reference conversion, or an unboxing conversion from `E` to `T`**, or if either `E` or `T` is an open type**. It is a compile-time error if an expression of type `E` is not pattern compatible with the type in a type pattern that it is matched with. 26 | 27 | ## Drawbacks 28 | [drawbacks]: #drawbacks 29 | 30 | None. 31 | 32 | ## Alternatives 33 | [alternatives]: #alternatives 34 | 35 | None. 36 | 37 | ## Unresolved questions 38 | [unresolved]: #unresolved-questions 39 | 40 | None. 41 | 42 | ## Design meetings 43 | 44 | LDM considered this question and felt it was a bug-fix level change. We are treating it as a separate language feature because just making the change after the language has been released would introduce a forward incompatibility. Using the proposed change requires that the programmer specify language version 7.1. 45 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-04-11.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Apr 11, 2017 2 | 3 | ## Agenda 4 | 5 | 1. Runtime behavior of ambiguous default implementation 6 | 7 | 8 | # Runtime behavior of ambiguous default implementation 9 | 10 | The feature is intended to work when a new default-implemented member is added to an interface `I`, even when an implementing class `C` is not recompiled. So the runtime needs to know about default implementations and be able to find them. 11 | 12 | In the case of overrides, there may be diamond-hierarchy cases where the compiler knows of only one override, but one is added later to another interface. The implementations are now ambiguous, and a recompilation would cause an ambiguity, but it would seem desirable that the runtime should choose "the one the compiler knew about"; that, somehow, that knowledge would be baked in to the compiled class `C`. 13 | 14 | Starting out with these type declarations in separate assemblies: 15 | 16 | ``` c# 17 | interface I1 { void M() { Impl1 } } 18 | interface I2 : I1 { override void M() { Impl2 } } 19 | interface I3 : I1 { } 20 | class C : I2, I3 { } 21 | ``` 22 | 23 | Everyone's happy, and `C`'s implementation of `M` would unambiguously come from `I2`. 24 | 25 | Now `I3` is modified and recompiled: 26 | 27 | ``` c# 28 | interface I3 : I1 { override void M() { Impl3 } } 29 | ``` 30 | 31 | What should happen at runtime? When `C` was compiled, it "thought" everything was alright. At runtime it isn't. Can the compilation of `C` bake in a preference based on its understanding of the world at the time of compilation? Should it? 32 | 33 | It is not obvious how this would work. What if the default implementation `C` depends on is moved, deleted or overridden? Should it just be a "vague" preference in case of ambiguity, to get the runtime on the right track? 34 | 35 | This seems complicated, fragile and fraught with peril, but ending up with an ambiguity at runtime is also bad. 36 | 37 | Regardless, there will always be runtime ambiguities; "baking in" preferences would only address a subset. Two open questions: 38 | 39 | 1. Should we try to help resolve ambiguities by baking in compile time preferences? Unresolved. 40 | 2. Should we fail or pick an "arbitrary" implementation in case of inevitable ambiguities at runtime? Unresolved. Bad to error. Bad to run "arbitrary" code. 41 | 42 | We should look more deeply into what Java does here. There must be accumulated insight already on this topic. 43 | -------------------------------------------------------------------------------- /Communities.md: -------------------------------------------------------------------------------- 1 | **Disclaimer**: This document is maintained by the C# community and not the responsibility of the C# Language Design Team (LDT). Please do not contact the LDT for any errors in this document; however, PRs are welcome. 2 | 3 | **Channels:** 4 | 5 | - [Gitter](https://gitter.im/dotnet/csharplang) - Any discussion related to the C# language but mostly design discussions. 6 | 7 | [![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) 8 | 9 | - [Discord](https://aka.ms/csharp-discord) - Any discussion related to the C# language up to the application level. 10 | 11 | [![Join the chat at https://aka.ms/csharp-discord](https://img.shields.io/discord/102860784329052160.svg)](https://aka.ms/csharp-discord) 12 | 13 | - IRC - Any discussion related to the C# language up to the application level. 14 | 15 | [![Join the chat at https://www.irccloud.com/invite?channel=%23%23csharp&hostname=irc.freenode.net&port=6697&ssl=1](https://img.shields.io/badge/IRC-%23%23csharp-1e72ff.svg?style=flat)](https://www.irccloud.com/invite?channel=%23%23csharp&hostname=irc.freenode.net&port=6697&ssl=1) 16 | 17 | Servers: irc.freenode.net, chat.freenode.net 18 | 19 | Channel: ##csharp 20 | 21 | [![Join the chat at https://www.irccloud.com/invite?channel=%23c%23&hostname=irc.quakenet.org&port=6697&ssl=1](https://img.shields.io/badge/IRC-%23c%23-1e72ff.svg?style=flat)](https://www.irccloud.com/invite?channel=%23c%23&hostname=irc.quakenet.org&port=6697&ssl=1) 22 | 23 | Servers: irc.quakenet.org 24 | 25 | Channel: #c# 26 | 27 | Recommended IRC Clients: HexChat, mIRC. 28 | 29 | **Forums:** 30 | 31 | - [Stack Overflow](https://stackoverflow.com) 32 | 33 | Please read [this](https://stackoverflow.com/help/dont-ask) before posting. 34 | 35 | - [MSDN Forum](https://social.msdn.microsoft.com/Forums/vstudio/en-US/home?forum=csharpgeneral) 36 | 37 | Please read [this](https://social.msdn.microsoft.com/Forums/vstudio/en-US/df14dc5f-982b-4676-b767-6123c8a90495/where-is-the-correct-place-to-make-posts-regarding-aspnet-sql-or-another-topic-that-is-not-related?forum=csharpgeneral) before posting. 38 | 39 | - [Reddit](https://www.reddit.com/r/csharp/) 40 | 41 | Please read [this](https://www.reddit.com/r/csharp/comments/3xn6sm/welcome_to_rcsharp_read_this_post_before) before posting. 42 | -------------------------------------------------------------------------------- /meetings/2015/LDM-2015-05-25.md: -------------------------------------------------------------------------------- 1 | # C# Design Meeting Notes for May 25, 2015 2 | 3 | Discussion for these notes can be found in https://github.com/dotnet/roslyn/issues/3912. 4 | 5 | ## Agenda 6 | 7 | Today we went through a bunch of the proposals on GitHub and triaged them for our list of features in issue #2136. Due to the vastness of the list, we needed to use *some* criterion to sort by, and though far from ideal we ordered them by number of comments, most to least. 8 | 9 | Here's where we landed, skipping things that were already "Strong interest". Some are further elaborated in sections below. 10 | 11 | 1. Method contracts <*Stay at "Some interest"*>(#119) 12 | 2. Destructible types <*Stay at "Probably never"*> (#161) 13 | 3. Params IEnumerable <*Stay at "Small but Useful*>(#36) 14 | 4. Multiple returns <*Addressed by tuples. Close.*> (#102) 15 | 5. More type inference <*Not a coherent proposal. Close*> (#17) 16 | 6. Readonly parameters and locals <*Stay at "Some interest"*>(#115) 17 | 7. Implicitly typed lambdas <*Add at "Probably never"*>(#14) 18 | 8. Immutable types <*Stay at "Some interest*>(#159) 19 | 9. Object initializers for immutable objects <*Add at "Some interest"*>(#229) 20 | 10. First item is special <*Add at "Never"*>(#131) 21 | 11. Array slices <*Keep at "Interesting but needs CLR support"*>(#120) 22 | 12. Vararg calling convention <*Merge with params IEnumerable*>(#37) 23 | 13. XML Literals <*Add to "Never"*>(#1746) 24 | 14. Local Functions <*Move to "Some interest*>(#259) 25 | 15. Covariant returns <*Stay at "Some interest*>(#357) 26 | 27 | # Params IEnumerable 28 | 29 | This needs more thinking - let's not just implement the straightforward design. There are perf issues, for instance, around implementing through the `IEnumerable` interface instead of arrays directly. 30 | 31 | # More type inference 32 | 33 | Not a coherent proposal. But even if there was one, we probably wouldn't want it in C#. 34 | 35 | # Implicitly typed lambdas 36 | 37 | These are mostly subsumed by local functions, which we'd rather do. It has some individual usefulness but not much synergy. 38 | 39 | # Object initializers for immutable objects 40 | 41 | We want to think this together with withers, not sure what form it would take. 42 | 43 | # first item in loops is special 44 | 45 | We recognize the scenario but it's not worthy of a feature. 46 | 47 | # vararg calling convention 48 | 49 | Roll it in with params IEnumerable discussion for investigation. 50 | 51 | # XML literals 52 | 53 | Never! We won't bake in a specific format. 54 | 55 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-02-07.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Feb 7, 2018 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | # Tuple equality 6 | 7 | We want to make it so that the `==` operator is delegated to the elements, but the choice of `&` operator shouldn't be up to the elements. 8 | 9 | We can't think of this in terms of a user-defined `==` on the `ValueTuple<...>` overloads, since those don't have access to the specific `==` implementations of the type arguments. 10 | 11 | 1. Same as `(tempA == tempC) && (tempB == tempD)`, taking whatever `&` operator eventually gets used. 12 | 2. Same as `(bool)(tempA == tempC) && (bool)(tempB == tempD)` but only when there is an implicit conversion to `bool` 13 | 2a. Same as 2 or `!(tempA == tempC).false && !(tempB == tempD).false`, so there are two ways to make the individual comparisons `bool` 14 | 15 | We want to do 2a, so that we make every effort to turn the result of each comparison into bool. For `==` we would use the `false` operator, for `!=` we will use the true operator. But the `&` and `|` are applied to booleans. 16 | 17 | For dynamic, let's look at what `if` does and probably do the same. 18 | 19 | 20 | # The fixed statement 21 | 22 | We're adding support for a type to have a special method that returns a pinned ref. 23 | 24 | ## Copy? 25 | 26 | Should that special method be executed on a copy or on an original l-value? We don't see good reasons to. 27 | - The method might want to change the state for the benefit of a future `fixed` or otherwise 28 | - Wasteful to copy big struct 29 | 30 | ## Generics 31 | 32 | We need to know if it's a struct or a class, to decide whether to copy (for the null check) or not (to preserve mutations in a struct). 33 | 34 | We already solved this for `?.`. We can emit a check for whether the type is a reference type (check whether its default is null), and the JIT specializes. 35 | 36 | ## Nullable 37 | 38 | No lifting. You can do your own if you really want, but there is no way (for the compiler or user) to expose the value itself without copying. If you want to pin an specific nullable type you can, as long as an extension method is provided for it. 39 | 40 | ## Ref extension methods 41 | 42 | Allow? Yes, same as for ref extension methods in general: Has to be called on a mutable l-value. For `in`, anything is fine. 43 | 44 | ## Name of method 45 | 46 | `GetPinnableReference` is weird enough; we don't have to put "Dangerous" or something in the name. 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-08-16.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Aug 16, 2017 2 | 3 | *Quote of the day:* 4 | > "It's an open question whether we go out with a bang`!`" 5 | 6 | ## Agenda 7 | 8 | 1. The null-forgiving operator 9 | 10 | 11 | # The null-forgiving operator 12 | 13 | How exactly does the null-forgiving post-fix `!` operator work? 14 | 15 | Proposal: 16 | 17 | 1. *Target typed*: `e!` implicitly converts to `T` if `e` does, but without nullability warnings 18 | - `string s = null!;` 19 | - `string s = default!` 20 | - `string s = GetNameOrNull()!;` 21 | - `List l = GetList()!;` 22 | - `List l = GetList()!;` 23 | 2. *Inherent type*: if the type of `e` is a nullable reference type `T?`, then the inherent type of `e!` is `T` 24 | - `var s = GetNameOrNull()!;` 25 | - `GetNameOrNull()!.Length;` 26 | 3. *Default expressions*: if `T` is a non-nullable reference type, then `default(T)!` suppresses the warning normally given by `default(T)` 27 | 28 | For 2, an alternative is to have a dedicated `!.` and `![...]` operator, cousins of `?.` and `?[...]`. Then you wouldn't get to factor out to a local with `var`, though. 29 | 30 | 3 is a bit of a corner case. Most people would choose to just rewrite it to something else - there are plenty of options. But `default(T)` is a good strategy for code generators, so probably worth keeping the ability to silence that warning. 31 | 32 | We could generalize `!` to silencing all nullability warnings even in subexpressions. This seems ill-motivated, though, and there's no particular expectation that you want silencing in subexpressions at the same time you want it on the overall expression. 33 | 34 | If `!` is applied in a place that yields no nullability warnings, does that lead to a warning? No. We don't want to create a new source of warnings caused by a warning-suppressing operator! There is a legit scenario, which is to clean up superfluous "!"s when a depended-upon API gets properly annotated. But this seems more the province of analyzers or similar tools. 35 | 36 | We can make `!!` an error. If you really want two consecutive bangs (we don't believe there's *any* scenario, other than swearing) you can parenthesize: `(e!)!`. 37 | 38 | An alternative is to make the type of `e!` oblivious, if we choose to embrace a notion of oblivious. That's attractive in that it makes a type for "something that doesn't yield warnings", but it's also viral - could lead to many things not being checked. It's an option to be considered in future. 39 | 40 | ## Conclusion 41 | 42 | Follow the proposal. Make `!!` an error. -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-09-05.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for September 5, 2018 2 | 3 | ## Agenda 4 | 5 | 1. Index operator: is it a unary operator? 6 | 1. Compiler intrinsics 7 | 8 | # Discussion 9 | 10 | ## Index operator 11 | 12 | There are multiple questions here: 13 | 14 | 1. Is the operator syntactically a unary operator? 15 | 1. Is it a user-definable operator? 16 | 1. Does it have the same precedence as other unary operators? 17 | 1. Do members which do not exist implicitly exist anyway as an intrinsic? 18 | 1. Does it have overloads? Is `^^1` allowed? That would imply that there's an 19 | overload which takes an index. 20 | 21 | Follow-up: is `..` a binary operator? 22 | 23 | **Conclusion** 24 | 25 | Agreed that it's syntactically a unary operator. Also agreed that it is a 26 | semantically treated as a unary operator that it is not user-definable. Right 27 | now, we don't see a great need to add a second overload. There is a single 28 | overload for `int`. 29 | 30 | We're not strictly defining `..` as a binary operator right now. It has its 31 | own syntactic form. 32 | 33 | Also, we're renaming '^' to the "hat" operator. 34 | 35 | ## Compiler intrinsics 36 | 37 | Proposal: https://github.com/dotnet/csharplang/blob/master/proposals/intrinsics.md 38 | 39 | There is contention between using `void*` and some stronger typing, either a 40 | delegate or some kind of function pointer syntax. The benefit of using a 41 | pointer type is that it is always unsafe, which this feature requires if 42 | there are no allocations (because an appdomain could unload and cause the 43 | invocation to point to arbitrary memory). 44 | 45 | For `calli`, there's a worry about moving the typing from the point of 46 | retrieving a function pointer to the declaration of the target and calling 47 | convention. The `extern` declaration, specifically, is disliked. 48 | 49 | Does this only work for managed code or also for native code? Do we have to 50 | care about the calling convention? 51 | 52 | **Conclusion** 53 | 54 | We'd probably be willing to accept this in some form. We think the current 55 | proposal needs some work. 56 | 57 | Some thoughts/suggestions: 58 | 59 | 1. Don't allow instance `&receiver.M` form -- only types for instance methods 60 | e.g., `&TypeName.M`. 61 | 1. Drop the separate declaration for the `calli` invocation. A special calling 62 | form like `_calli_stdcall(args, f)` is suggested. We don't like signature 63 | being declared somewhere other than the method definition or at the call site. 64 | 1. Would like the calling convention, if used, present at the call site. -------------------------------------------------------------------------------- /meetings/2019/LDM-2019-01-16.md: -------------------------------------------------------------------------------- 1 | 2 | # C# Language Design Notes for Jan. 16th, 2019 3 | 4 | ## Agenda 5 | 6 | 1. Shadowing in lambdas 7 | 2. pattern-based disposal in `await foreach` 8 | 9 | ## Discussion 10 | 11 | ### Shadowing in nested functions 12 | 13 | *Q: Allow shadowing for all lambdas as well?* 14 | 15 | **A**: Yes. 16 | 17 | *Q: Allow shadowing with range variables in LINQ queries?* 18 | 19 | ```C# 20 | // char c; 21 | var q = from c in s 22 | from c2 in s 23 | where c != ' ' 24 | select c; 25 | var q2 = s 26 | .SelectMany(c => s, (c, c2) => new { c, c2 }) 27 | .Where(_x => _x.c != ' ') 28 | .Select(_x => _x.c); 29 | ``` 30 | 31 | `c` and `c2` can't be named the same because they are equivalent to two 32 | parameters being named the same. However, should the new `c` be able to 33 | shadow the local `c`? 34 | 35 | **A**: Let's look at the implementation and decide based on the complexity. 36 | 37 | ### Pattern-based disposal in `await foreach` 38 | 39 | `WithCancellation` and `ConfigureAwait` both return a custom type that does 40 | not implement the interface, just the pattern. `await foreach` does not pick 41 | it up because it does not have pattern-based disposal and the type cannot 42 | implement the `IAsyncDisposable` type because it does not produce a `ValueTask` 43 | return for disposal. 44 | 45 | Proposals: 46 | 47 | 1. Just allow pattern-based disposal for `IAsyncDisposable`. Only allow 48 | pattern-based disposable for `IDisposable` in ref structs. 49 | 50 | a. For `await foreach`, use the pattern, then dynamically check for 51 | interface and use it if present. 52 | 53 | b. For `await foreach`, use the pattern, then statically check for the 54 | interface and use it if present. 55 | 56 | The dynamic check in (1a) is used because in C# 1.0 the non-generic `IEnumerator` 57 | did not implement IDisposable. 58 | 59 | The next question is whether to consider extension methods. The standard pattern 60 | we've previously settled on is: 61 | 62 | 1. Check for pattern 63 | 2. Check for interface 64 | 3. Check for extension methods 65 | 66 | Can we use this pattern for `IAsyncDisposable`? The primary question is whether 67 | to consider extension methods. 68 | 69 | **Conclusion** 70 | 71 | Let's do 1b. We do not have a non-generic `IAsyncEnumerator` that doesn't 72 | implement `IAsyncDisposable`, so the dynamic check is unnecessary. 73 | 74 | We will not consider extension methods for `IAsyncDisposable` or 75 | `IDisposable` (on ref structs). We will consider extension methods for 76 | `IAsyncEnumerable` and `IEnumerable`. 77 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-10-10.md: -------------------------------------------------------------------------------- 1 | 2 | # C# Language Design Notes for Oct 10, 2018 3 | 4 | _QOTD: C# has a long and proud tradition of being oblivious_ 5 | 6 | ## Agenda 7 | 8 | 1. Pattern matching open questions 9 | 10 | ## Discussion 11 | 12 | All questions are in [this issue](https://github.com/dotnet/csharplang/issues/1054) 13 | 14 | ### Short discard diagnostics 15 | 16 | **Conclusion** 17 | 18 | We've taken breaking changes like this before, but there's more risk to 19 | this one because this code is legal all the way back to C# 1.0. This will 20 | not be an error -- an underscore in a case block with a constant `_` in 21 | scope will match the constant. A warning wave warning should be added 22 | to make matching a constant named `_` a warning. 23 | 24 | ### Nullable reference types vs switch analysis 25 | 26 | In general, the nullable analysis computes very similar information to 27 | switch exhaustiveness. It would be strange if the switch produced 28 | information contrary to the nullable analysis. There are some fundamental 29 | language semantic problems with making switch (and pattern) exhaustiveness 30 | and nullable analysis depend on each other. One possibility may be to 31 | perform the analysis for both of these situations simultaneously. We don't 32 | think that the exhaustiveness analysis doesn't affect the nullable analysis 33 | directly. Alternatively, we can phrase exhaustiveness as exclusive of 34 | nullable reference types and let the null analysis handle switch 35 | exhaustiveness for null specifically. Nullable value types would be handled 36 | as part of traditional exhaustiveness. 37 | 38 | **Conclusion** 39 | 40 | Let's explore the separation of exhaustiveness between null and non-null, st. 41 | all exhaustiveness warnings do not consider null, and warnings related to null 42 | are delayed until nullable analysis. 43 | 44 | Also, regardless of whether or not we generate a warning for the following case, 45 | `i` is not definitely assigned at the end: 46 | 47 | ```C# 48 | int i; 49 | switch (s) 50 | { 51 | case string t: i = 0; break; 52 | } 53 | Console.WriteLine(i); // is i definitely assigned? 54 | ``` 55 | 56 | ### Permit optionally omitting the pattern on the last branch of switch expr 57 | 58 | **Conclusion** 59 | 60 | Rejected. 61 | 62 | ### Should exhaustiveness affect definite assignment? 63 | 64 | **Conclusion** 65 | 66 | Confirmed the current behavior. 67 | 68 | ### Switch expression as statement expression 69 | 70 | **Conclusion** 71 | 72 | We like it. Not sure it will make it for C# 8.0. 73 | 74 | ### Single-element positional deconstruction 75 | 76 | **Conclusion** 77 | 78 | We need to think about 1-tuples again. -------------------------------------------------------------------------------- /meetings/2019/LDM-2019-02-25.md: -------------------------------------------------------------------------------- 1 | 2 | # C# Language Design Notes for Feb 25th, 2019 3 | 4 | ## Agenda 5 | 6 | Semantics of `base()` calls in default interface implementations and classes 7 | 8 | ## Discussion 9 | 10 | The exact semantics of `base()` are still unresolved. Consider 11 | the following legacy `base` call. 12 | 13 | ```C# 14 | class A 15 | { 16 | virtual void M() {} 17 | } 18 | 19 | class B : A 20 | { 21 | // override void M() { } 22 | } 23 | 24 | class C : B 25 | { 26 | override void M() => base.M(); 27 | } 28 | ``` 29 | 30 | The behavior for `base` is to find the "nearest" implementation and call 31 | that implementation using a direct call, meaning if B.M is uncommented it 32 | will be called, while if it commented out then A.M will be called. Notably, 33 | if `B` is uncommented at compile time, but at runtime the `B.M` override is 34 | not present *the runtime will call A.M.* This is because the runtime will 35 | continue looking through base classes for a matching signature if the target 36 | method is not present. 37 | 38 | Most importantly, the runtime *does not* yet have this behavior for `base()` 39 | calls in interface implementations. This is because there could be multiple 40 | paths to search down and the current IL encoding does not provide a root 41 | definition to search towards. 42 | 43 | For example, 44 | 45 | ```C# 46 | interface IA 47 | { 48 | void M(); 49 | } 50 | interface IB : IA 51 | { 52 | void IA.M() // If this gets removed, the IC.M call will fail 53 | { 54 | base(IA).M(); 55 | } 56 | } 57 | interface IC : IB 58 | { 59 | void IA.M() => base(IB).M(); 60 | } 61 | ``` 62 | 63 | At the moment, we do not have the time to implement an entire new IL form 64 | for `base()` calls, so we have to implement a behavior in absence of that 65 | feature. 66 | 67 | Choices: 68 | 69 | 1. No `base()` call 70 | 2. `base()` call can only target the original definition of the method 71 | 3. `base(T).M()` call is a direct call to `T` and an error if the method 72 | doesn't exist 73 | 4. `base(T)` starts searching in `T`, but in the compiler looks at the bases 74 | for a unique, most derived implementation. 75 | 76 | For feature evolution, we then have three more choices later. 77 | 78 | 1. Stay as-is 79 | 2. New opcode/behavior for `base()` 80 | 3. New opcode/behavior for `base.` and `base()` 81 | 82 | **Conclusion** 83 | 84 | For the first choice, let's do (3). The emitted code will be a direct call to 85 | that method. It is expected that the runtime will throw an exception if an 86 | implementation is not present in that type. For binding, in classes the 87 | signature used will be the closest override, while for interfaces the 88 | signature will be the member definition. -------------------------------------------------------------------------------- /spec_cn/classes_cn.md: -------------------------------------------------------------------------------- 1 | # 类 2 | 3 | 类是一种可以包含数据成员、函数成员和嵌套类型的数据结构。类的数据成员包括常量和字段。类的函数成员包括方法、属性、事件、索引器、操作符、实例构造器和析构器、静态构造器。类类型支持继承。藉由继承这种机制,派生类能够对基类进行扩展和特殊化。 4 | 5 | ## 类的声明 6 | 7 | *class_declaration*是一种*type_declaration*([类型声明](namespaces.md#type-declarations))),它用于声明一个新的类。 8 | 9 | ```antlr 10 | class_declaration 11 | : attributes? class_modifier* 'partial'? 'class' identifier type_parameter_list? 12 | class_base? type_parameter_constraints_clause* class_body ';'? 13 | ; 14 | ``` 15 | 16 | *class_declaration*包含了一组可选的*attributes*([特征](attributes.md)),后面跟上一组可选的*class_modifier*([类修饰符](classes.md#class-modifiers)),再跟上可选的`partial`修饰符,再跟上(必须出现的)关键字`class`和一个用来给类命名的*identifier*,再跟上一个可选的*type_parameter_list*([类型参数](classes.md#type-parameters)),再跟上一个可选的*class_base*定义([Class base specification](classes.md#class-base-specification)),再跟上一组可选的*type_parameter_constraints_clause*([类型参数约束](classes.md#type-parameter-constraints)),再跟上(必须有的)*class_body*([类体](classes.md#class-body)),最后是一个可选的分号(`;`)。 17 | 18 | 仅当类的声明中带有*type_parameter_list*的时候,才能为其配有*type_parameter_constraints_clause*。 19 | 20 | 带有*type_parameter_list*的类声明称为**泛型类声明**。进而,任何一个嵌套于泛型类声明或者泛型结构体声明中的类声明,也会是泛型的——因为外层类型的类型参数是被用于完整构建一个类型的。(译注:这里需要代码解释。???) 21 | 22 | ### 类修饰符 23 | 24 | *class_declaration*中可以包含一组可选的类修饰符: 25 | 26 | ```antlr 27 | class_modifier 28 | : 'new' 29 | | 'public' 30 | | 'protected' 31 | | 'internal' 32 | | 'private' 33 | | 'abstract' 34 | | 'sealed' 35 | | 'static' 36 | | class_modifier_unsafe 37 | ; 38 | ``` 39 | 40 | 如果同一个修饰符在类声明中出现多次,就会产生一个编译期错误。 41 | 42 | `new`修饰符(译注:这里的`new`不是操作符)被允许用来修饰嵌套类。`new`修饰符的作用是告诉(派生类)使用被`new`修饰符所修饰的成员去隐藏一个继承来的同名成员(参见[`new`修饰符](classes.md#the-new-modifier))。如果`new`修饰符出现在非嵌套类的声明中,就会产生一个编译期错误。 43 | 44 | 修饰符`public`、`protected`、`internal`和`private`用于控制类的(可)访问性。基于类声明出现位置的上下文,某几个访问性控制操作符会不被允许([访问性声明](basic-concepts.md#declared-accessibility))。 45 | 46 | 修饰符`abstract`、`sealed`和`static`会在随后章节中详细讨论。 47 | 48 | #### 抽象类 49 | 50 | 修饰符`abstract`的作用是指明被它修饰的类是不完整的。抽象类作为不完整的类,其存在的意义(旨在),是只能被当作基类来使用。抽象类与非抽象类的区别在于: 51 | 52 | * 抽象类不能被直接实例化(译注:还能间接实例化的?),如果使用`new`操作符去实例化一个抽象类,就会产生一个编译期错误。然而,使用编译期类型为抽象类型的变量或者值是可以的,这样的变量和值必须要么是`null`要么包含着对非抽象类实例的引用,这里的非抽象类实例必须是派生自前述的抽象类型。 53 | * 抽象类允许(但不一定)包含抽象成员。(译注:抽象成员必须声明在抽象类中。) 54 | * 抽象类不能是封闭的。(译注:即不能同时使用`abstract`和`sealed`来修饰同一个类。) 55 | 56 | 当一个非抽象类派生自一个抽象类的时候,这个非抽象类必包含对所有继承来的抽象成员的实际实现,借此重写这些抽象成员。下面的例子中: 57 | ```csharp 58 | abstract class A 59 | { 60 | public abstract void F(); 61 | } 62 | 63 | abstract class B: A 64 | { 65 | public void G() {} 66 | } 67 | 68 | class C: B 69 | { 70 | public override void F() { 71 | // actual implementation of F 72 | } 73 | } 74 | ``` 75 | 抽象类`A`引入了一个抽象方法`F`。类`B`又引入了一个方法`G`,但由于它并没有提供对`F`的实现,所以类`B`也必须声明为抽象的。因为类`C`中并没有抽象成员,所以类`C`被允许(但并不一定要)成为非抽象的。 -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-05-16.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for May 16, 2017 2 | 3 | ## Agenda 4 | 5 | 1. Triage C# 7.1 features that didn't make it 6 | 2. Look at C# 7.2 features 7 | 3. GitHub procedure around new design notes and proposals 8 | 4. Triage of championed features 9 | 10 | 11 | # Triage C# 7.1 features that didn't make it 12 | 13 | ## Private protected 14 | 15 | Almost there, push to 7.2 for completion. 16 | 17 | ## Field-targeted attributes 18 | 19 | This is a bug fix level change, and very useful. Push to 7.2 20 | 21 | 22 | # Look at C# 7.2 features 23 | 24 | "Slicing" needs to be split into "ref structs" and "slicing syntax". 25 | 26 | Push out everything, except 27 | - things that are "in theme" - related to refs, spans, etc. 28 | - things that are almost done (field-targeted attributes, private protected) 29 | 30 | Keep everything that's in theme until we can sit with the `Span` folks and prioritize. 31 | 32 | 33 | # GitHub procedure around new design notes and proposals 34 | 35 | We should use issues as a notification mechanism for when design notes and proposals are completed. Revisions are comments on the same issue. 36 | 37 | (This has now been adopted for design notes back to Mar 15, 2017.) 38 | 39 | 40 | # Triage of championed features 41 | 42 | ## CallerArgumentExpression 43 | 44 | Push to 7.X and start process for getting the attribute in place. 45 | 46 | ## Leading and trailing digit "separators" 47 | 48 | ``` c# 49 | M(0b_1, 0x_A) 50 | ``` 51 | 52 | Very low additional value. Would probably help some code generators, and some code layout. Might accept a pull request. 53 | 54 | ## Static delegates 55 | 56 | Delegates require, generally, 2 allocations, and are crappy for interop. Static delegates are typed `IntPtr`s, essentially. 57 | `ValueAction` and `ValueFunc`. Lot of asks from CoreRT, for PInvoke etc. Of course they won't be able to close over anything, so they'd require significant language support. 58 | 59 | ## Namespace XML doc comments 60 | This is hard to take as a community contribution: it's primarily about the IDE behavior. Not all of that may even be open source today. 61 | It would also affect the rest of the ecosystem, which would now have to handle it. Need to coordinate with IDE team. 62 | 63 | ## `??` and `?.` for pointers 64 | 65 | Probably lo-pri to make this pleasant. But nothing against it. This seems suitable for up-for-grabs. 66 | 67 | The syntax should probably be `?->`. For double pointers, you're out of options! 68 | 69 | 70 | ## Non-trailing named arguments 71 | 72 | Helps selectively use parameter names for readability. Would relieve people from bending over backwards to put name-prone parameters last. Also would make it work better with params. 73 | 74 | Would need some ide work. 75 | 76 | 77 | -------------------------------------------------------------------------------- /proposals/csharp-8.0/null-coalescing-assignment.md: -------------------------------------------------------------------------------- 1 | # null coalescing assignment 2 | 3 | * [x] Proposed 4 | * [ ] Prototype: Not Started 5 | * [ ] Implementation: Not Started 6 | * [ ] Specification: Below 7 | 8 | ## Summary 9 | [summary]: #summary 10 | 11 | Simplifies a common coding pattern where a variable is assigned a value if it is null. 12 | 13 | As part of this proposal, we will also loosen the type requirements on `??` to allow an expression whose type is an unconstrained type parameter to be used on the left-hand side. 14 | 15 | ## Motivation 16 | [motivation]: #motivation 17 | 18 | It is common to see code of the form 19 | 20 | ```csharp 21 | if (variable == null) 22 | { 23 | variable = expression; 24 | } 25 | ``` 26 | 27 | This proposal adds a non-overloadable binary operator to the language that performs this function. 28 | 29 | There have been at least eight separate community requests for this feature. 30 | 31 | ## Detailed design 32 | [design]: #detailed-design 33 | 34 | We add a new form of assignment operator 35 | 36 | ``` antlr 37 | assignment_operator 38 | : '??=' 39 | ; 40 | ``` 41 | 42 | Which follows the [existing semantic rules for compound assignment operators](../../spec/expressions.md#compound-assignment), except that we elide the assignment if the left-hand side is non-null. The rules for this feature are as follows. 43 | 44 | Given `a ??= b`, where `A` is the type of `a`, `B` is the type of `b`: 45 | 46 | 1. If `A` does not exist or is a non-nullable value type, a compile-time error occurs. 47 | 2. If `B` is not implicitly convertible to `A`, a compile-time error occurs. 48 | 3. The type of `a ??= b` is `A`. 49 | 4. `a ??= b` is evaluated at runtime as `a ?? (a = b)`, except that `a` is only evaluated once. 50 | 51 | For the relaxation of the type requirements of `??`, we update the spec where it currently states that, given `a ?? b`, where `A` is the type of `a`: 52 | 53 | > 1. If A exists and is not a nullable type or a reference type, a compile-time error occurs. 54 | 55 | We relax this requirement to: 56 | 57 | 1. If A exists and is a non-nullable value type, a compile-time error occurs. 58 | 59 | ## Drawbacks 60 | [drawbacks]: #drawbacks 61 | 62 | As with any language feature, we must question whether the additional complexity to the language is repaid in the additional clarity offered to the body of C# programs that would benefit from the feature. 63 | 64 | ## Alternatives 65 | [alternatives]: #alternatives 66 | 67 | The programmer can write `(x = x ?? y)`, `if (x == null) x = y;`, or `x ?? (x = y)` by hand. 68 | 69 | ## Unresolved questions 70 | [unresolved]: #unresolved-questions 71 | 72 | - [ ] Requires LDM review 73 | - [ ] Should we also support `&&=` and `||=` operators? 74 | 75 | ## Design meetings 76 | 77 | None. 78 | -------------------------------------------------------------------------------- /proposals/csharp-7.3/auto-prop-field-attrs.md: -------------------------------------------------------------------------------- 1 | # Auto-Implemented Property Field-Targeted Attributes 2 | 3 | ## Summary 4 | [summary]: #summary 5 | 6 | This feature intends to allow developers to apply attributes directly to the backing fields of auto-implemented properties. 7 | 8 | ## Motivation 9 | [motivation]: #motivation 10 | 11 | Currently it is not possible to apply attributes to the backing fields of auto-implemented properties. In those cases where the developer must use a field-targeting attribute they are forced to declare the field manually and use the more verbose property syntax. Given that C# has always supported field-targeted attributes on the generated backing field for events it makes sense to extend the same functionality to their property kin. 12 | 13 | ## Detailed design 14 | [design]: #detailed-design 15 | 16 | In short, the following would be legal C# and not produce a warning: 17 | 18 | ```csharp 19 | [Serializable] 20 | public class Foo 21 | { 22 | [field: NonSerialized] 23 | public string MySecret { get; set; } 24 | } 25 | ``` 26 | 27 | This would result in the field-targeted attributes being applied to the compiler-generated backing field: 28 | 29 | ```csharp 30 | [Serializable] 31 | public class Foo 32 | { 33 | [NonSerialized] 34 | private string _mySecretBackingField; 35 | 36 | public string MySecret 37 | { 38 | get { return _mySecretBackingField; } 39 | set { _mySecretBackingField = value; } 40 | } 41 | } 42 | ``` 43 | 44 | As mentioned, this brings parity with event syntax from C# 1.0 as the following is already legal and behaves as expected: 45 | 46 | ```csharp 47 | [Serializable] 48 | public class Foo 49 | { 50 | [field: NonSerialized] 51 | public event EventHandler MyEvent; 52 | } 53 | ``` 54 | 55 | ## Drawbacks 56 | [drawbacks]: #drawbacks 57 | 58 | There are two potential drawbacks to implementing this change: 59 | 60 | 1. Attempting to apply an attribute to the field of an auto-implemented property produces a compiler warning that the attributes in that block will be ignored. If the compiler were changed to support those attributes they would be applied to the backing field on a subsequent recompilation which could alter the behavior of the program at runtime. 61 | 1. The compiler does not currently validate the AttributeUsage targets of the attributes when attempting to apply them to the field of the auto-implemented property. If the compiler were changed to support field-targeted attributes and the attribute in question cannot be applied to a field the compiler would emit an error instead of a warning, breaking the build. 62 | 63 | ## Alternatives 64 | [alternatives]: #alternatives 65 | 66 | ## Unresolved questions 67 | [unresolved]: #unresolved-questions 68 | 69 | ## Design meetings 70 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-10-25.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Oct 25, 2017 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | 6 | 7 | ## #98 8 | 9 | ## 34 and 35 10 | 11 | 34 should bot be concurrent safe, just like the other compound assignment. 12 | 13 | Bundle with 8.0, but could push out. Seems to align with nullable reference types 14 | # 32 15 | 16 | Reconcile 32 with 1020 17 | 18 | 19 | Criteria: 20 | 21 | - Loose ends 22 | - External expectation 23 | 24 | # ref as iteration variable 25 | 26 | Not currently allowed, should probably have a proposal (Andy) 27 | 28 | # 185 keep in 3 to prioritize 29 | 30 | # 45 31 | 32 | Push out to 8.0 for realism, but still prioritize design time 33 | 34 | # 933, 1046, and uninitialized ref local 35 | 36 | These should happen together in 7.3 37 | 38 | # 111 Punt to 8.X 39 | 40 | # 1020 946 945 keep 41 | 42 | # 882 pattern-related, goto 8.0 43 | 44 | # 435 45 | Keep in 7.3, see if we can settle design 46 | 47 | # 190 48 | 49 | Relatively obvious design, with some gnarly bits (dynamic, conversion) 50 | 51 | Usability gap with tuples let's keep it. 52 | 53 | # 189 54 | 55 | Let is more important than from. It lets you use out variables 56 | 57 | ``` c# 58 | from s in strings 59 | let t = (b: int.TryParse(out var n), n) 60 | where t.b 61 | select t.n 62 | ``` 63 | Could be 64 | ``` c# 65 | from s in strings 66 | let (b, i) = (int.TryParse(out var n), n) 67 | where b 68 | select i 69 | ``` 70 | 71 | There's a bit of design work, especially if we also want the from clause. 72 | 73 | We could allow out vars but not the deconstruction, and it would still be useful. 74 | 75 | Could save dec for later. It's actually orthogonal. 76 | 77 | Action: 78 | 79 | Carve out deconstruction, push to 8.X 80 | 81 | # 187 82 | 83 | On the brink, but keeping for now; need to be convinced of value 84 | 85 | # 185 86 | 87 | Keep pushing on it, got to get the train going on `Range` 88 | 89 | `x..y` does new Range(x, y) or Range.Create(x, y) 90 | 91 | Consider whether it should be a new kind of operator instead. 92 | 93 | # 104 94 | 95 | Micro-feature: Just allow `System.Enum` as a constraint 96 | Mini-feature: allow `enum` as a constraint, translate to `System.Enum, struct` 97 | 98 | Keep this in, but it is very cuttable. 99 | 100 | # 98 101 | 102 | It's a zero-conceptual-overhead feature. 103 | 104 | Original designers left in space between meanings for a purpose. But that's not so compelling to us anymore. 105 | 106 | Because it touches overload resolution, it might be better aligned with a .0 release. But we're not compelled by that. 107 | 108 | Let's keep it, but again, it's cuttable. 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-05-23.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for May 23, 2018 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | # Working with data 6 | 7 | Collections of data with heterogeneous types. 8 | 9 | It's *not* objects, but decoupled from operations. 10 | 11 | - tuples 12 | - anonymous types 13 | - classes 14 | 15 | All have a mutability issue, just different ways. 16 | 17 | Modeling enumerated types is complex and doesn't provide exhaustiveness. 18 | 19 | Also, you cannot efficiently switch over different types. You do tricks like visitors, abstract kind properties, etc. Performance, correctness and succinctness are all in conflict. Type patterns help, because they look good, but they are still not as efficient or as safe as they could be. 20 | 21 | Technically speaking, mutability and value equality are a dangerous combo on classes. If the object mutates, its equality and hashcode change, meaning you could lose track of them in dictionaries, etc. 22 | 23 | On the other hand, C# is mutable by default: should we bend over backwards to not support this combo? 24 | 25 | Object initializers aren't strictly necessary, but they jive well with `with` expressions, give a less positional view. 26 | 27 | Object initializers are really popular today, in that they are used a lot. But it's unclear whether they are because the declaration site doesn't provide constructors, and instead do the "easy" thing of just offering auto-properties. 28 | 29 | Withers: we keep talking about them for readonly data, but they might very well be useful on mutable data as well. 30 | 31 | The "data classes" proposal puts weight on *not* being positional. It could even reorder members alphabetically in generated positional constructs such as constructors. 32 | 33 | Separately, there is an idea of "named tuples". These are what have previously been proposed as "records". These are an evolution story for tuples. 34 | 35 | This may be an overload of concepts. It may be that we can view them as aspects of the same feature; one may be an evolution of the other. 36 | 37 | Discriminated syntax: Enum classes. Starts very simple, probably has ways of letting you grow up. That gets messy though, with the nesting, but we could use partial to separate things. 38 | 39 | Kind fields must necessarily be unspeakable, and discoverable only by the compiler. Otherwise they can't be efficient. 40 | 41 | Records and data classes are extensions of existing constructs. Enum classes are more of a new concept. 42 | 43 | We kind of agree on the simple cases. It's how they grow up and fit into the existing world that's fraught with questions. 44 | 45 | Interestingly, you sometimes want your "enums" *not* to be exhaustive. This is the case when you expect it to evolve with more cases. 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /proposals/covariant-returns.md: -------------------------------------------------------------------------------- 1 | # covariant return types 2 | 3 | * [x] Proposed 4 | * [ ] Prototype: Not Started 5 | * [ ] Implementation: Not Started 6 | * [ ] Specification: Not Started 7 | 8 | ## Summary 9 | [summary]: #summary 10 | 11 | Support _covariant return types_. Specifically, allow an overriding method to have a more derived reference type than the method it overrides. 12 | 13 | ## Motivation 14 | [motivation]: #motivation 15 | 16 | It is a common pattern in code that different method names have to be invented to work around the language constraint that overrides must return the same type as the overridden method. See below for an example from the Roslyn code base. 17 | 18 | ## Detailed design 19 | [design]: #detailed-design 20 | 21 | Support _covariant return types_. Specifically, allow an overriding method to have a more derived reference type than the method it overrides. This would apply to methods and properties, and be supported in classes and interfaces. 22 | 23 | This would be useful in the factory pattern. For example, in the Roslyn code base we would have 24 | 25 | ``` cs 26 | class Compilation ... 27 | { 28 | virtual Compilation WithOptions(Options options)... 29 | } 30 | ``` 31 | 32 | ``` cs 33 | class CSharpCompilation : Compilation 34 | { 35 | override CSharpCompilation WithOptions(Options options)... 36 | } 37 | ``` 38 | 39 | The implementation of this would be for the compiler to emit the overriding method as a "new" virtual method that hides the base class method, along with a _bridge method_ that implements the base class method with a call to the derived class method. 40 | 41 | ## Drawbacks 42 | [drawbacks]: #drawbacks 43 | 44 | - [ ] Every language change must pay for itself. 45 | - [ ] We should ensure that the performance is reasonable, even in the case of deep inheritance hierarchies 46 | - [ ] We should ensure that artifacts of the translation strategy do not affect language semantics, even when consuming new IL from old compilers. 47 | 48 | ## Alternatives 49 | [alternatives]: #alternatives 50 | 51 | We could relax the language rules slightly to allow, in source, 52 | 53 | ```csharp 54 | abstract class Cloneable 55 | { 56 | public abstract Cloneable Clone(); 57 | } 58 | 59 | class Digit : Cloneable 60 | { 61 | public override Cloneable Clone() 62 | { 63 | return this.Clone(); 64 | } 65 | 66 | public new Digit Clone() // Error: 'Digit' already defines a member called 'Clone' with the same parameter types 67 | { 68 | return this; 69 | } 70 | } 71 | ``` 72 | 73 | ## Unresolved questions 74 | [unresolved]: #unresolved-questions 75 | 76 | - [ ] How will APIs that have been compiled to use this feature work in older versions of the language? 77 | 78 | ## Design meetings 79 | 80 | None yet. There has been some discussion at . -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-11-29.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Nov 29, 2017 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | # Match expression 6 | 7 | Current proposal: 8 | 9 | - Infix vs prefix `switch` 10 | - `switch` vs `match` 11 | - curlies or parens for body 12 | - `case` or not 13 | - arrows? 14 | - discards or defaults? 15 | - also need to remember when clauses 16 | 17 | If it's too similar to switch statements, then it sets certain expectations. If it's too different, it's not utilizing existing intuition. 18 | 19 | Should it be a verb/command? Not many expression keywords are that (`select` is an exception, though). 20 | 21 | `case` is heavyweight, but helps visually separate the issues. 22 | 23 | ``` c# 24 | state = (state, action) switch ( 25 | (DoorState.Closed, Action.Open) => DoorState.Opened, 26 | (DoorState.Opened, Action.Close) => DoorState.Closed, 27 | (DoorState.Closed, Action.Lock) => DoorState.Locked, 28 | (DoorState.Locked, Action.Unlock) => DoorState.Closed, 29 | _ => state); 30 | 31 | state = match (state, action) 32 | { 33 | (DoorState.Closed, Action.Open) => DoorState.Opened, 34 | (DoorState.Opened, Action.Close) => DoorState.Closed, 35 | (DoorState.Closed, Action.Lock) => DoorState.Locked, 36 | (DoorState.Locked, Action.Unlock) => DoorState.Closed, 37 | _ => state 38 | }; 39 | 40 | state = switch (state, action) 41 | { 42 | case (DoorState.Closed, Action.Open): DoorState.Opened 43 | case (DoorState.Opened, Action.Close): DoorState.Closed 44 | case (DoorState.Closed, Action.Lock): DoorState.Locked 45 | case (DoorState.Locked, Action.Unlock): DoorState.Closed 46 | case _: state 47 | }; 48 | ``` 49 | 50 | The last one is subject to ambiguity-like situations between expression and statement `switch`. 51 | 52 | We should also consider nesting of match expressions. 53 | 54 | No matter what syntax we choose, we'll get requests for doing more things in expressions. We can live with that. 55 | 56 | Parens look too much like a list of *expressions*. 57 | 58 | Arrows make it look like lambda expressions. 59 | 60 | ## Decisions 61 | 62 | We agree that we will not use the keyword `default`. You can use `_`, and in the rare case where that's defined, you can use `var _`. 63 | 64 | We like curly braces for the grouping. 65 | 66 | The rest is up in the air. We'll stay with the first version for now in the prototype, other than the curly braces. 67 | 68 | 69 | 70 | # Where and when can identifier appear? 71 | 72 | 73 | 74 | # Syntax for property patterns 75 | 76 | Not urgent 77 | 78 | ``` c# 79 | 80 | ``` -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-01-11.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Jan 11, 2017 2 | 3 | *Raw notes - need cleaning up* 4 | 5 | ## Agenda 6 | 7 | - Language aspects of [compiler intrinsics](https://github.com/dotnet/roslyn/issues/11475) 8 | 9 | Intrinsics 10 | 11 | Two compiler behaviors: 12 | 1. Recognize declarations as intrinsics, and implement them 13 | 2. Enforce whichever special rules apply on call 14 | 15 | Could grow the list over time. 16 | 17 | Scenario: avoid il rewrite or inefficient impls 18 | 19 | Lets you do cross-platform libraries. 20 | 21 | This feels cheap, and limits the implact to just the compiler. 22 | 23 | Can't be abstracted out into generic methods - at least without combinatorial explosion 24 | 25 | Some might be obsoleted over time as language features ("static delegates"?) come along. 26 | 27 | Could allow optionally specifying the intrinsic name in the attribute, so you could call the method something else if you like 28 | 29 | Open question: local functions. 30 | 31 | Wouldn't emit to metadata. 32 | 33 | How supported should this be, as a language feature? Should semantic analysis understand this, and give you good live feedback? Doesn't need a lot of tooling support out of the gate; it's a feature for a very small set of 34 | 35 | 36 | 37 | 38 | ``` c# 39 | switch(...) 40 | { 41 | case string s when .... x1: 42 | case int i when .... x2 & capture x1: 43 | case 1 when ... capture x1; 44 | break 45 | 46 | case string s when .... x3: 47 | case int i when .... x4: 48 | case 1 when .... & capture something 49 | break; 50 | } 51 | ``` 52 | 53 | The code needed to be surprised by the lifetime of expression variables in case headers being the whole switch block is quite convoluted (just like this sentence). 54 | 55 | ``` c# 56 | case 1 => WriteLine(...); 57 | case 2 58 | { 59 | 60 | } 61 | 62 | case string s when (...) 63 | { 64 | ... 65 | } 66 | ``` 67 | 68 | - Broaden scope and lifetime of expr variables to whole switch block 69 | - Broaden lifetime of expr variables, but not the scope 70 | - Limit scope of variables in bodies of cases that are "new" 71 | - 72 | 73 | 74 | When there are expression variables in the case header, in the case body forbid: 75 | - ref locals 76 | - to expr variables 77 | - local functions 78 | - that capture case-section expr variables 79 | - labels 80 | - that are jumped to from other case sections 81 | 82 | Drop the subbullets for now, and maybe we can relax later 83 | 84 | 85 | Out of all these approaches, we still think expanding the lifetime (but not the scope) has the lowest risk. Fallout work is likely in the debugger, which will have a suboptimal experience in these scenarios. This work is probably puntable. 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-10-16.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Oct 16, 2017 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | 6 | "Being on the same floor as SPJ is a good way to shake out difficult corner cases" 7 | 8 | 1. LINQ 9 | 2. Shapes 10 | 11 | 12 | # Applying to LINQ 13 | 14 | Mixed results. 15 | 16 | Looking for perf, and ways to do more selective specialization. 17 | 18 | ## Sum 19 | 20 | Specialized to some numeric types, but not all. 500 lines of code. In those, the loop is unspecialized too. 21 | 22 | One page of code! 23 | 24 | A new design dimension: should I use interfaces or concepts: pay for abstraction or pay for specialization 25 | 26 | Generic soup: this may be a superficial design issue, or even tooling issue. 27 | 28 | For instance, the `AssociatedType`s, other languages allow them to be retrieved by dot notation. Jeremy Siek paper "Associated Types and ...". `TColl.TEnum` etc. 29 | 30 | From experience, abstracting over enumerators tends to need associated types or higher-kinded types. 31 | 32 | Shouldn't be too discouraged by being smoked by LINQOptimizer. That one optimizes big queries, but what keeps people away from LINQ is more the death by a thousand paper cuts of using LINQ all the time. Roslyn avoids things that allocate, which today means not using LINQ. THis could be the thing that would allow it to. 33 | 34 | ## Select 35 | 36 | The return type is associated. The problem is that adding new instances can change the return type from afar, upsetting the consuming code. 37 | 38 | Improves by 2/3rds when the array specialization is used. 39 | 40 | ## SelectMany 41 | 42 | The generic type inference gets very messy here. It shows that concept inference needs to be interleaved with type inference in a way that we are still only loosely grasping. 43 | 44 | This is a place where LINQ allocates a lot, whereas this allocates hardly anything. We go at .75 the time even unspecialized. Also, the specialized version is twice as fast as the unspecialized. 45 | 46 | It shows that if you open up for specializations to be plopped in, there's quite a lot to gain. 47 | 48 | ## Conclusion 49 | 50 | Some promise on optimization. Pinches of salt here and there. The approach definitely seems to have promise. 51 | 52 | More tests to do. 53 | 54 | 55 | # Shapes 56 | 57 | Concepts can tie in to the richness of expression in C# around different kinds of operations (operators, conversions, constructors...) 58 | 59 | Interesting to consider whether there's more of a specialization relationship between concepts and instances, rather than a type/instance relationship. 60 | 61 | If this went further, there's a very large laundry list. 62 | 63 | 64 | 65 | 66 | 67 | 68 | # Conclusion 69 | 70 | We *really* would like to be able to do the post-hoc implementation of concepts. This 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-02-21.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Feb 21, 2018 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | 6 | ## Agenda 7 | 8 | Various big and small issues around nullable reference types, preparing for upcoming prototypes 9 | 10 | 11 | # How are explicit casts interpreted? 12 | 13 | ``` c# 14 | (object)null // object?, with warning 15 | (object?)string.Empty // object 16 | (IEnumerable)new[]{string.Empty} // IEnumerable 17 | (IComparer)Comparer.Default // IComparer, with warning 18 | ``` 19 | 20 | For the top two, there are two approaches you can think of: 21 | 22 | - nullability should be inferred from the expression 23 | - the cast represents an intent and we should honor it 24 | 25 | It's to some degree a tension between existing code or the right design for new code. 26 | 27 | If we were to infer nullability (rather than take it from the type), would nullability problems always be caught later on? 28 | 29 | ``` c# 30 | object o = ...; 31 | var o = (object)...; 32 | 33 | class X { object[] o; } 34 | ``` 35 | 36 | If we make it more lax, then there's more of a disconnect between top-level nullability and nested nullability. 37 | 38 | If we think of `?` not so much as a type thing but an observation on what's there, it doesn't seem so onerous to have it tracked locally. 39 | 40 | Another approach: Have it be a different warning. Then you can switch it off separately, for legacy purposes. 41 | 42 | For the casts: 43 | 44 | 45 | ``` c# 46 | (string)null; // A 47 | M((string)null);// B 48 | M((string?)x); // C 49 | M((string?)F()) // D - F is unannotated, and I want to impose my understanding of what it is 50 | M((string)F()) // E - F is unannotated, and I want to impose my understanding of what it is 51 | ``` 52 | 53 | In C you want to treat the argument as may-be-null, regardless of what is in x. 54 | In B you could have a warning that is off in legacy code 55 | 56 | Assume M is generic, and we make type inference based on arguments. Then D and E both make sense - they influence the result type of M, maybe. We shouldn't wave these off, but they are in some sense separable. 57 | 58 | In the prototype we could give you the choice. We could have it on by default, and the warning could tell you how to turn it off. 59 | 60 | C is either useless, or it means "forget inferred nullability". And it won't occur in old code. 61 | 62 | Let's expand B: 63 | 64 | ``` c# 65 | M((string)y); // 66 | ``` 67 | 68 | Say this is legacy, and now M gets upgraded to take `string?`. And `y` is now possibly null. The warning on `(string)y` could be one of those that is turned off for legacy purposes. 69 | 70 | Between two options: 71 | 72 | A: locals are implicitly nullable 73 | B: locals need explicit annotations like everything else, there's a concession to legacy 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /meetings/2019/LDM-2019-01-07.md: -------------------------------------------------------------------------------- 1 | 2 | # C# Language Design Notes for January 7th, 2019 3 | 4 | ## Agenda 5 | 6 | Nullable: 7 | 8 | 1. Variance in overriding/interface implementation 9 | 2. Breaking change in parsing array specifiers 10 | 11 | ## Discussion 12 | 13 | ### variance in overriding/interface implementation 14 | 15 | Issues: https://github.com/dotnet/roslyn/issues/23268, https://github.com/dotnet/roslyn/issues/30958 16 | 17 | We currently don't support overriding or inheritance with co/contravariance in nullability. 18 | 19 | Overriding: we mostly take the signature from the overriding member, not the base. 20 | 21 | We could allow nullability variance and then use the most derived implementation. 22 | 23 | The problem is if we don't provide a warning on further derivation, another deriver can 24 | "re-loosen the contract." 25 | 26 | There doesn't seem to be any problem with an interface implementation, because the safety is provided 27 | in the actual code. 28 | 29 | Note: if you override an object-returning method with a dynamic-returning method, we currently 30 | use the dynamic return on callsites, which is a parallel to how nullable works. 31 | 32 | Options: 33 | 34 | 1. Keep stringent invariant rule 35 | 2. Allow safe variation on interface implicit implementation only 36 | 3. Allow on overrides also, check against base override 37 | 38 | **Conclusion** 39 | 40 | General agreement on (3). Possibly change tuple names to operate similarly 41 | (use the base's override as the authoritative signature). We will also use 42 | the same rules for explicit interface implementation. 43 | 44 | ### Breaking change in parsing array specifiers 45 | 46 | Issue: https://github.com/dotnet/roslyn/issues/32141 47 | 48 | ```C# 49 | a ? x is A[][] ? b : c 50 | // ^ Is this a nullable annotation or part of a ?: 51 | ``` 52 | 53 | Options: 54 | 55 | 1. Keep decision find another solution for ambiguity 56 | a. always prefer ternary for back compat, use parens for new meaning in `is`/`as` 57 | 2. Change array decision 58 | a. `?` is a type constructor that's applicable to arrays 59 | b. Alternate syntax: `?` inside brackets: `a[?][,]` and `a[][,?]` 60 | 61 | One of the main problems with changing the array is: `new string[3][];` In 62 | this example, `3` refers to the outer array size, but if we make `string[][]?` 63 | mean "top-level nullable," there's either an inconsistency between the 64 | question mark location (`new string[3]?[]` means *inner* array is nullable) 65 | or we swap the location of the rank (`new string[]?[3]`). 66 | 67 | **Conclusion** 68 | 69 | Let's try to stick with our original design, and add tie breakers to parsing 70 | to prefer ternary in any ambiguity. Right now we know of problems with array 71 | types in `is` and `as`. There may be more expressions which can end with an 72 | array type, which we will address as we find them. 73 | 74 | *Update: This decision was revised in the Jan. 9th meeting.* -------------------------------------------------------------------------------- /proposals/declaration-expressions.md: -------------------------------------------------------------------------------- 1 | # Declaration expressions 2 | 3 | Support declaration assignments as expressions. 4 | 5 | ## Motivation 6 | [motivation]: #motivation 7 | 8 | Allow initialization at the point of declaration in more cases, simplifying code, and allowing `var` to be used. 9 | 10 | ```csharp 11 | SpecialType ReferenceType => 12 | (var st = _type.SpecialType).IsValueType() ? SpecialType.None : st; 13 | ``` 14 | 15 | Allow declarations for `ref` arguments, similar to `out var`. 16 | 17 | ```csharp 18 | Convert(source, destination, ref List diagnostics = null); 19 | ``` 20 | 21 | ## Detailed design 22 | [design]: #detailed-design 23 | 24 | Expressions are extended to include declaration assignment. Precedence is the same as assignment. 25 | 26 | ```antlr 27 | expression 28 | : non_assignment_expression 29 | | assignment 30 | | declaration_assignment_expression // new 31 | ; 32 | declaration_assignment_expression // new 33 | : declaration_expression '=' local_variable_initializer 34 | ; 35 | declaration_expression // C# 7.0 36 | | type variable_designation 37 | ; 38 | ``` 39 | 40 | The declaration assignment is of a single local. 41 | 42 | The type of a declaration assignment expression is the type of the declaration. 43 | If the type is `var`, the inferred type is the type of the initializing expression. 44 | 45 | The declaration assignment expression may be an l-value, for `ref` argument values in particular. 46 | 47 | If the declaration assignment expression declares a value type, and the expression is an r-value, the value of 48 | the expression is a copy. 49 | 50 | The declaration assignment expression may declare a `ref` local. 51 | There is an ambiguity when `ref` is used for a declaration expression in a `ref` argument. 52 | The local variable initializer determines whether the declaration is a `ref` local. 53 | 54 | ```csharp 55 | F(ref int x = IntFunc()); // int x; 56 | F(ref int y = RefIntFunc()); // ref int y; 57 | ``` 58 | 59 | The scope of locals declared in declaration assignment expressions is the same the scope of corresponding declaration expressions from C#7.0. 60 | 61 | It is a compile time error to refer to a local in text preceding the declaration expression. 62 | 63 | ## Alternatives 64 | [alternatives]: #alternatives 65 | No change. This feature is just syntactic shorthand after all. 66 | 67 | More general sequence expressions: see [#377](https://github.com/dotnet/csharplang/issues/377). 68 | 69 | To allow use of `var` in more cases, allow separate declaration and assignment of `var` locals, 70 | and infer the type from assignments from all code paths. 71 | 72 | ## See also 73 | [see-also]: #see-also 74 | See Basic Declaration Expression in [#595](https://github.com/dotnet/csharplang/issues/595). 75 | 76 | See Deconstruction Declaration in the [deconstruction](https://github.com/dotnet/roslyn/blob/master/docs/features/deconstruction.md) feature. 77 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-01-17.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Jan 17, 2017 2 | 3 | ## Agenda 4 | 5 | A few C# 7.0 issues to review. 6 | 7 | 1. Constant pattern semantics: which equality exactly? 8 | 2. Extension methods on tuples: should tuple conversions apply? 9 | 10 | # Constant pattern semantics 11 | 12 | [Issue #16513](https://github.com/dotnet/roslyn/issues/16513) proposes a change to the semantics of constant patterns in `is` expressions. For the code 13 | 14 | ``` c# 15 | e is 42 16 | ``` 17 | 18 | We currently generate the call `object.Equals(e, 42)` (or equivalent code), but we should instead generate `object.Equals(42, e)`. 19 | 20 | The implementation of `object.Equals` does a few reference equality and null checks, but otherwise delegates to the instance method `Equals` of its *first* argument. So with the current semantics the above would call `e.Equals(42)`, whereas in the proposal we would call `42.Equals(e)`. 21 | 22 | The issue lists several good reasons, and we can add more to the list: 23 | 24 | - The constant pattern isn't very *constant*, when it's behavior is determined by the non-constant operand! 25 | - Optimization opportunities are few when we cannot depend on known behavior of calling `c.Equals` on a constant value. 26 | - Intuitively, the pattern should do the testing, not the object being tested 27 | - Calling a method on the expression could cause side effects! 28 | - The difference from switch semantics is jarring 29 | - Switching would preserve the nice property of `is` expressions today that it only returns `true` if the left operand is implicitly convertible to the (type of the) right. 30 | 31 | There really is no downside to this, other than the little bit of work it requires to implement it. 32 | 33 | ## Conclusion 34 | 35 | Do it. 36 | 37 | 38 | # Extension methods on tuples 39 | 40 | [Issue #16159](https://github.com/dotnet/roslyn/issues/16159) laments the facts that extension methods only apply to tuples if the tuple types match exactly. This is because extension methods currently only apply if there is an *identity, reference or boxing conversion* from the receiver to the type of the extension method's first parameter. 41 | 42 | The spirit of this rule is that if it applies to a type or its bases or interfaces, it will work. We agree that it *feels* like it should also work for tuples - at least "sometimes". We cannot make it just always work for tuple conversions, though, since they may recursively apply all kinds of conversions, including user defined conversions. 43 | 44 | We could check *recursively* through the tuple type for "the right kind of conversion". Compiler-wise this is a localized and low-risk change. It makes tuples compose well with extension methods. It's another place where things should "distribute over the elements" of the tuple. 45 | 46 | This is a now-or-never kind of change. It would be a breaking change to add later. 47 | 48 | ## Conclusion 49 | 50 | Try to do it now if at all possible. 51 | -------------------------------------------------------------------------------- /proposals/csharp-7.0/local-functions.md: -------------------------------------------------------------------------------- 1 | # Local functions 2 | 3 | We extend C# to support the declaration of functions in block scope. Local functions may use (capture) variables from the enclosing scope. 4 | 5 | The compiler uses flow analysis to detect which variables a local function uses before assigning it a value. Every call of the function requires such variables to be definitely assigned. Similarly the compiler determines which variables are definitely assigned on return. Such variables are considered definitely assigned after the local function is invoked. 6 | 7 | Local functions may be called from a lexical point before its definition. Local function declaration statements do not cause a warning when they are not reachable. 8 | 9 | TODO: _WRITE SPEC_ 10 | 11 | ## Syntax grammar 12 | 13 | This grammar is represented as a diff from the current spec grammar. 14 | 15 | ```diff 16 | declaration-statement 17 | : local-variable-declaration ';' 18 | | local-constant-declaration ';' 19 | + | local-function-declaration 20 | ; 21 | 22 | +local-function-declaration 23 | + : local-function-header local-function-body 24 | + ; 25 | 26 | +local-function-header 27 | + : local-function-modifiers? return-type identifier type-parameter-list? 28 | + ( formal-parameter-list? ) type-parameter-constraints-clauses 29 | + ; 30 | 31 | +local-function-modifiers 32 | + : (async | unsafe) 33 | + ; 34 | 35 | +local-function-body 36 | + : block 37 | + | arrow-expression-body 38 | + ; 39 | ``` 40 | 41 | Local functions may use variables defined in the enclosing scope. The current 42 | implementation requires that every variable read inside a local function be 43 | definitely assigned, as if executing the local function at its point of 44 | definition. Also, the local function definition must have been "executed" at 45 | any use point. 46 | 47 | After experimenting with that a bit (for example, it is not possible to define 48 | two mutually recursive local functions), we've since revised how we want the 49 | definite assignment to work. The revision (not yet implemented) is that all 50 | local variables read in a local function must be definitely assigned at each 51 | invocation of the local function. That's actually more subtle than it sounds, 52 | and there is a bunch of work remaining to make it work. Once it is done you'll 53 | be able to move your local functions to the end of its enclosing block. 54 | 55 | The new definite assignment rules are incompatible with inferring the return 56 | type of a local function, so we'll likely be removing support for inferring the 57 | return type. 58 | 59 | Unless you convert a local function to a delegate, capturing is done into 60 | frames that are value types. That means you don't get any GC pressure from 61 | using local functions with capturing. 62 | 63 | ### Reachability 64 | 65 | We add to the spec 66 | 67 | > The body of a statement-bodied lambda expression or local function is considered reachable. 68 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-06-13.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Jun 13, 2017 2 | 3 | ## Agenda 4 | 5 | 1. Native-size ints 6 | 2. Native-size floats 7 | 8 | 9 | # Native-size ints 10 | 11 | We want `nint` and `nuint` to be part of the language. They will be used in contexts where we care about respecting checked vs unchecked in operator behavior, for example. 12 | 13 | We have two options for how to represent at runtime: 14 | 15 | 1. Project to `IntPtr`, track the `nint` or `nuint` "overlay" in metadata for compile-time consumption only 16 | 2. Project to new struct types that wrap an `IntPtr` and potentially exhibit different behavior at runtime too 17 | 18 | Option 2 had some objections attached. Let's go through them: 19 | 20 | - Adoption/roll out of new types would take time 21 | - We could embed these struct types in the generated assembly and use the NoPIA machinery to unify them across assemblies. 22 | - Would have a slight cost on IL size. 23 | - Probably not a decisive objection 24 | - Need new overloads for a few things, such as `Interlocked.CompareExchange` 25 | - We could probably live with those rolling out gradually, especially with access to the `IntPtr` of a `nint` or `nuint` as a public field (so it can be passed by `ref` to existing overloads) 26 | - There is a bifurcation because the types are really distinct at runtime. There's interop pain between them. 27 | - This may be a good thing. Arguably they represent different concepts: `IntPtr` is more of an opaque handle, whereas `nint` and `nuint` are real integers with number semantics. 28 | 29 | Option 1 has some objections as well: 30 | 31 | - `ToString` doesn't work right 32 | - This could probably be fixed. It is unlikely that the current deficiencies are depended upon 33 | - you lose context on box/unbox 34 | - That is already the case today with other common types, such as tuples, nullable value types and `dynamic`. Is it really so bad? 35 | - Imprecise reflection info 36 | - Again, we live with this in many places 37 | 38 | `String.Format` would exhibit the combination of the first and second problem: passing a `nint` as an argument, it would box and lose context, so `IntPtr.ToString` would get called. 39 | 40 | Things to ponder regardless of implementation strategy: 41 | 42 | - **Conversions:** Should there be conversions between `nint`/`nuint` and `IntPtr`? If so, which way (if any) should be implicit? 43 | - **Upgrade:** Someone using `IntPtr` today might want to switch to `nint`. This would mostly just work, but would have very subtle changes of behavior here and there. 44 | 45 | ## Conclusion 46 | We are still hung on what to do. We want to more deeply understand objections against Option 1, and understand if mitigations considered would address e.g. Xamarin's concerns. 47 | 48 | 49 | # Native-size floats 50 | 51 | Float really is a different discussion. Language embedding is less significant: we don't have a notion of checked/unchecked. The main objective would just be to have an alias. -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-11-20.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Nov 20, 2017 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | 6 | ## Agenda 7 | 8 | 9 | # Recursive pattern matching 10 | 11 | ## Grammar 12 | 13 | The grammar splits out to a couple of cases so that the right things are optional etc. directly in the grammar. 14 | 15 | That separation is also there in the representation of the implementation, currently. An alternative is to have a single production and call it out in prose. There are advantages to having a separated representation, in that your code can make more assumptions about what's there or not. 16 | 17 | ## Names in deconstruction pattern 18 | 19 | What is the utility? Current proposal it's only to guarantee that you get the thing you say. It does not allow reordering or leaving out elements. 20 | 21 | This pattern should by and large work like deconstruction. But it's plausible to have names here, even if we don't in deconstruction; the argument would be a symmetry with constructors, which are sometimes used with names and optional arguments. 22 | 23 | ## Should the identifier be restricted from being a discard? 24 | 25 | Since it can be left out completely? No, it's probably good to allow the discard. For refactoring etc. 26 | 27 | ## Matching via ITuple 28 | 29 | It's "too likely" that the compiler would consider that a thing *may* 30 | implement `ITuple`. 31 | 32 | We'll restrict to static types `object`, `ITuple` and any type that derives from `ITuple` and has no deconstructors. 33 | 34 | `dynamic` is treated just like `object`. We don't go looking for deconstructors dynamically. 35 | 36 | 37 | ## Syntactic ambiguity around parenthesized expression 38 | 39 | Should we even have single-element deconstruction patterns? 40 | 41 | Could require some other element to disambiguate, e.g. `(2) _`. 42 | 43 | This would raise the cost of adding single-element tuples and deconstruction in the future, at least if they have a syntax *other* than parenthesized expressions (e.g., `(x,)`). 44 | 45 | ``` c# 46 | switch(my1DPoint) 47 | case 1DPoint(0): 48 | ... 49 | case 1DPoint(var x): 50 | ... 51 | ``` 52 | 53 | Compromise position: Allow a single one only if there is a type in front. It gives the obvious symmetry with a single-element constructor, without restricting the design space for future single-element tuples or pattern grouping constructs. 54 | 55 | ## Cast ambiguity 56 | 57 | Now went away. It's a cast, or an error. 58 | 59 | ## Short discard 60 | 61 | Yes, allow `_` as a pattern in and of itself. It would not be allowed at the top level in an `is` expression. (That is allowed today, and designates the type `_`). 62 | 63 | It actually means something different than `default` in a switch, because it gets an error if there are no cases left. That seems useful. 64 | 65 | So: allow it everywhere except at the top level in an is-expression. 66 | 67 | ## Colon or is? 68 | 69 | 70 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-03-19.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Mar 19, 2018 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | ## Agenda 6 | 7 | Triage 8 | 9 | # 7.3 10 | 11 | Moved undone 7.3 features to 8.0. 12 | 13 | # Allow default in deconstruction 14 | 15 | We allowed for `==`, so it seems we should also allow it for assignment and initialization. 16 | 17 | Should probably not require `ValueTuple`, but that's more of a compiler thing. 18 | 19 | 20 | ## Conclusion 21 | 22 | The feature is a good idea. Even though it's simple, we should not rush it into 7.3. 8.0. 23 | 24 | ``` c# 25 | case Customer { MiddleName: "" or null } 26 | ``` 27 | 28 | # and, or and not patterns 1350 29 | `not` feels useful. `and` is hard to come up with scenarios for. `or` has more scenarios, but may be better served by a `in { 1, 2, 3 }` style pattern. 30 | 31 | ``` c# 32 | if (x is not string s) { ... } 33 | else { ... /* s */ } 34 | ``` 35 | 36 | `not` would cause definite assignment to "flip". Should `s` be disallowed in a `not` pattern. 37 | 38 | The `{}` pattern for not null is a bit cryptic. `not null` would certainly be more direct. 39 | 40 | ## Conclusion 41 | 42 | Punt to 8.x, when we know more of the rest of the pattern story. 43 | 44 | 45 | # partial type inference #1349 46 | 47 | We've talked about this many times in the past, but couldn't settle on a syntax that was nice enough and worth its while. 48 | 49 | A 4th option: allow M(...) to match an M with more type parameters. But that probably goes against intuition and would complicate overload resolution. 50 | 51 | For 2: Just commas is known from typeof, so that concept is already in the language, though you can't mix "there" and "not there" today. 52 | 53 | ## Conclusion 54 | 55 | 8.X is when we'll look at it again. 56 | 57 | 58 | # Permit `t is null` for unconstrained type parameter #1284 59 | 60 | For null we special case type parameters in all other kinds of places, so this is the only place where you can't. 61 | 62 | Also, `t is 3`. We allow *type* patterns, but not constant patterns. 63 | 64 | ## Conclusion 65 | 66 | Absolutely should do this. Mark as 8.0. 67 | 68 | 69 | # Implicitly scoped using #114 #1174 70 | 71 | We have sympathy for this syntactic sugar, and we'd be interested in allowing it. It seems related to similar features, such as "defer" statements (avoids nesting `try/finally`). 72 | 73 | Also fits nicely with the work we already did with expression variable scoping. 74 | 75 | Also namespace without curlies, where you just say `namespace X.Y;` and it's in force for the remainder of the file. 76 | 77 | ## Conclusion 78 | 79 | Discuss at 8.X. Could get pulled up to 8.0. 80 | Let's also put defer and namespace into 8.X for consideration then. 81 | 82 | # User-defined positional patterns 83 | 84 | A next step from recursive patterns. 85 | 86 | ## Conclusion 87 | 88 | Let's revisit in 8.X when recursive patterns have played out. 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-10-18.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Oct 18, 2017 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | 6 | ## Agenda 7 | 8 | 9 | # 189 10 | 11 | ``` c# 12 | from s in strings 13 | let (b, i) = (int.TryParse(s, out var x), x) 14 | where b 15 | select i 16 | ``` 17 | 18 | Problem is that out vars also aren't yet allowed. 19 | 20 | We don't allow declaration expressions in 21 | 22 | 1. queries 23 | 2. constructor initializers 24 | 3. field/property initializers 25 | 26 | All of these were because we didn't settle the scope question. Time to do that. 27 | 28 | Let's put these as issues. And let's put those issues as 7.3. And let's put this one as 7.3. 29 | 30 | 31 | # 100 32 | 33 | The difficulty of this depends on the level of ambition. It has to play in to applicability of overloads this is passed to. If we allow member initializers, then we need to bind those 34 | 35 | ``` c# 36 | M(new { X = 7, Y = new { A = "Hello" } }); 37 | 38 | x = new () { Y = { e1, e2 } }; 39 | ``` 40 | 41 | If we're lucky, this is just about whether the conversion exists or not. 42 | 43 | Would there be a way that this would even influence generic type inference? 44 | 45 | ``` c# 46 | M(() => new (1)); 47 | ``` 48 | 49 | We'd need to think about this. 50 | 51 | It may be that there's a subset of the feature that's simpler. We would need that subset to not preclude going further later. 52 | 53 | Spooky action at a distance 54 | 55 | ``` c# 56 | M(Foo) 57 | M(Goo) 58 | 59 | M(new (1)) 60 | ``` 61 | 62 | Goo has a constructor that takes an int. Adding such a constructor to Foo will break the code. 63 | 64 | Now, adding a constructor is equivalent to adding a conversion in terms of the breaks it can entail. 65 | 66 | May also need more betterness rules. 67 | 68 | 69 | It could be that it's better to limit the feature so it does not participate in type inference and betterness, and can always be checked as a conversion. It might even be worth considering it only specifically to where one target type is known (so no overload applicability). 70 | 71 | 72 | For now, let's put it in 8.0. We don't believe it's going to make 7.3, but that makes us still consider it for design time. 73 | 74 | 75 | # 179 76 | 77 | For people who care about perf, they already need 3 or 4 overloads, and this would be yet another overload people will yell at them to add. 78 | 79 | For no parameters today, you no longer need an overload to avoid allocation, because we now use `Array.Empty`. 80 | 81 | `params IEnumerable` would be even less performant than the array one, because enumeration allocates. 82 | 83 | The point of it more is that if I want to take an `IEnumerable` anyway, then it's a convenience to add params and take the arguments individually. 84 | 85 | 86 | Not important enough to prioritize time for the design soon. Let's make this X.X. 87 | 88 | 89 | # How to continue 90 | 91 | Scan 7.X for remaining 7.3 items and ignore the rest for a bit. 92 | 93 | 94 | -------------------------------------------------------------------------------- /meetings/2019/LDM-2019-02-20.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Feb 20th, 2019 2 | 3 | ## Agenda 4 | 5 | - Nullable Reference Types: Open LDM Issues https://github.com/dotnet/csharplang/issues/2201 6 | 7 | ## Discussion 8 | 9 | There are variants of this scenario with `string!` and `string~`. The question is whether we should learn in both branches, or whether we should treat one branch as unreachable. 10 | 11 | ### 'Deliberate' Tests for Nullability 12 | 13 | This is a continuation of the discussion from the [last meeting](LDM-2019-02-13.md). 14 | 15 | Proposal: divide into "pure" and "not-pure" null checks. The "pure" 16 | null checks will affect both branches, but the "not-pure" will only 17 | affect one branch. Precisely: when a "pure" check is used, the 18 | nullability state is split for both branches, while a non-pure check 19 | does not have a "maybe-null" state after split. 20 | 21 | Proposed deliberate checks: 22 | 23 | 1. `x == null` 24 | 25 | 2. `x != null` 26 | 27 | 3. `(Type)x == null` 28 | 29 | 4. `(Type)x != null` 30 | 31 | 5. `x is null` 32 | 33 | 6. `s is string` (when the type of `s` is `string`) 34 | 35 | 7. `s is string s2` (when the type of `s` is `string`) 36 | 37 | 8. `s is string _` (when the type of `s` is `string`) 38 | 39 | 40 | Other checks: 41 | 42 | 1. `x is string` 43 | 44 | 2. `x is string s` 45 | 46 | 3. `x is C { Property = 3 }` 47 | 48 | 4. `TryGetValue` (`[NotNullWhenTrue]`) 49 | 50 | 5. `string.IsNullOrEmpty(s)` (`[NotNullWhenFalse]`) 51 | 52 | 6. `x?.ToString() != null` 53 | 54 | Follow up question: What about `?.` and related operators (e.g., `??`)? 55 | 56 | An example would be 57 | 58 | ```C# 59 | void M(string s) 60 | { 61 | if (s?.ToString() == null) 62 | { 63 | // is `s` maybe null? 64 | } 65 | } 66 | ``` 67 | 68 | A corresponding rewrite would be: 69 | 70 | ```C# 71 | void M(string s) 72 | { 73 | if (s == null || s.ToString() == null) 74 | { 75 | // s would be maybe-null after this point 76 | } 77 | } 78 | ``` 79 | 80 | **Conclusion** 81 | 82 | Pure: 83 | 84 | 1. `x == null` 85 | 86 | 2. `x != null` 87 | 88 | 3. `(Type)x == null` 89 | 90 | 4. `(Type)x != null` 91 | 92 | 5. `x is null` 93 | 94 | 6. `s is string` (when the type of `s` is `string`) 95 | 96 | 97 | All conditional access (`?.`, `??`) included 98 | 99 | Not-Pure 100 | 101 | 1. `x is string` 102 | 103 | 2. `x is string s` 104 | 105 | 3. `x is C { Property = 3 }` 106 | 107 | 4. `TryGetValue` (`[NotNullWhenTrue]`) 108 | 109 | 5. `string.IsNullOrEmpty(s)` (`[NotNullWhenFalse]`) 110 | 111 | 7. `s is string s2` (when the type of `s` is `string`) 112 | 113 | 8. `s is string _` (when the type of `s` is `string`) 114 | 115 | Switch statement/expression: switch statement treated as 116 | essentially the equivalent of an `if/else` rewrite. No 117 | decisions for the switch expression yet. -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-10-15.md: -------------------------------------------------------------------------------- 1 | 2 | # C# Language Design Notes for Oct 15, 2018 3 | 4 | ## Agenda 5 | 6 | 1. [Function pointers](https://github.com/dotnet/csharplang/blob/master/proposals/function-pointers.md) 7 | 2. [Readonly struct members](https://github.com/dotnet/csharplang/issues/1710) 8 | 3. Syntax for async streams 9 | 10 | ## Discussion 11 | 12 | ### Function pointers 13 | 14 | We're leaning towards no names. The encoding and scoping of the naming seems 15 | like it could be a problem. Not having names is not a new problem and it does 16 | not seem worse than far more common things like tuples. We many consider a 17 | richer augmentation of aliasing in general, which would help not only 18 | function pointers, but all the places where you may want to alias an existing 19 | type. 20 | 21 | Syntax: we played around with multiple funcptr type syntaxes. We haven't 22 | settled on a syntax yet, but we have been convinced by Jared that the 23 | `Func*` syntax is unworkable. If we later introduce a new structural 24 | function pointer type and syntax, we could evolve the current syntax to 25 | support the new syntax. 26 | 27 | *Q: Do we want the '&' when converting a method group to a `funcptr`?* 28 | 29 | One argument is that it's extra syntax at the place where the feature is 30 | least unsafe (at the declaration), instead of the place where's it's most 31 | unsafe (at the invocation). However, it is similar to syntax for other 32 | pointer types in the language and "feels right" when you see it. Let's keep 33 | it. 34 | 35 | **Conclusion** 36 | 37 | We like this approach a lot more than the previous attempt. The main item 38 | left is to make sure the syntax is unambiguous. 39 | 40 | ### Readonly struct members 41 | 42 | We discussed this feature back when we added "readonly" for struct 43 | definitions and decided we might consider it later. It is now later. 44 | 45 | Looking back on the language, we feel the silent copying of structs to avoid 46 | mutation is a bit of a wart. One thing stopping us from providing a warning 47 | is the lack of this feature, since sometimes you cannot mark a struct 48 | readonly just to avoid the copy. This feature would fill out the space enough 49 | that we could consider a warning wave or an analyzer to warn about silent 50 | struct copies. 51 | 52 | Examined a few syntaxes for this: 53 | 54 | - An attribute ([ReadonlyMethod]?) 55 | - `readonly` at the end 56 | - `readonly` at the beginning 57 | - Allow the explicit `this` parameter and let it be passed by `in` 58 | 59 | **Conclusion** 60 | 61 | We like the feature and think the syntax should be a `readonly` modifier at 62 | the beginning of the method, along with the other modifiers. 63 | 64 | ### Syntax of `foreach await`/`using await` 65 | 66 | `foreach await (...)` or `foreach async (...)` or `async foreach (...)`? 67 | 68 | In the past we've followed the pattern that there's always an explicit 69 | `await` in the code if we're awaiting. There's some debate on what sounds 70 | best. 71 | 72 | **Conclusion** 73 | 74 | `await foreach (...)` and `await using (...)`. 75 | -------------------------------------------------------------------------------- /meetings/2016/LDM-2016-07-12.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Jul 12, 2016 2 | ## Agenda 3 | 4 | Several design details pertaining to tuples and deconstruction resolved. 5 | # All or nothing with element names? 6 | 7 | There's certainly a case for partial names on literals - sometimes you want to specify the names of _some_ of the elements for clarity. These names have to fit names in the tuple type that the literal is being converted to: 8 | 9 | ``` c# 10 | List<(string firstName, string lastName, bool paid)> people = ...; 11 | 12 | people.Add(("John", "Doe", paid: true)); 13 | ``` 14 | 15 | Do we also want to allow partial names in _types_? 16 | 17 | Yes we do. We don't have strong arguments against it, and generality calls for it. It's likely that there are scenarios, and not allowing will feel arbitrary. "The `bool` deserves a clarifying name, but the `TypeSymbol` is clear". 18 | 19 | ``` c# 20 | var t = (1, y: 2); // infers (int, int y) 21 | (int x, int) t = (1, 2); 22 | ``` 23 | 24 | As a reminder, we distinguish between leaving out the name and explicitly specifying `Item1`, `Item2`, etc. You get a warning if you use `Item1`... in literals if they are not explicitly there in the target type. (Warning)` 25 | 26 | ``` c# 27 | (int, int) t = (Item1: 1, Item2: 2); // Warning, names are lost 28 | ``` 29 | 30 | For the editor experience we imagine offering completion of a named element in the corresponding position of a tuple literal. 31 | # ITuple 32 | 33 | ``` c# 34 | interface ITuple 35 | { 36 | int Size; 37 | object this[int i] { get; } 38 | } 39 | ``` 40 | 41 | `ITuple` is really an interface for dynamically checking for deconstructability. Whether we should add `ITuple` now depends on whether we think we want to allow recursive patterns to do that, once we add them. 42 | 43 | We _do_ think we want that. 44 | 45 | We cannot use an existing collection interface (such as `IReadOnlyCollection`) because we want to be able to distinguish between deconstructable objects and collections (which might one day get their own pattern). It is fine for a class to implement both. 46 | 47 | `ValueTuple<...>` should of course implement `ITuple`, and the translation of long tuples should work. 48 | 49 | The Framework team should chime in on the specific shape and names on the interface. Is `ITuple` the right name, or is it too narrow? Is `IDeconstructable` more to the point or is it too long? Should it be `Size` or `Length` or `Count`? 50 | # var when var type exists 51 | 52 | ``` c# 53 | class var {} 54 | var (x, y) = e; 55 | ``` 56 | 57 | People use this trick to block the use of `var`, and we should let them, even though we disagree with the practice. So even though a type isn't valid in that position, we will let the presence of a `var` type turn this into an error. 58 | # var method 59 | 60 | What if there's a _method_ called `var`? 61 | 62 | ``` c# 63 | ref int var(int x, int y); 64 | var(x, y) = e; // deconstruction or call? 65 | ``` 66 | 67 | Here there is no prior art, so we will always interpret the `var` to mean a deconstructing declaration. If you wanted the method call, parenthesize the call or use @. 68 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-02-26.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Feb 26 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | "We introduced off-by-two errors!" 6 | 7 | # Ranges 8 | 9 | Python has indexing from the end, as well as range endpoints from the end. The last element is -1. 10 | 11 | In ranges, the only way to express "to the end" is then to omit the end point (since they're also exclusive). 12 | 13 | There is no way to talk about the position at "length" in Python. We are concerned with that approach, because you can easily make an error where you compute "distance from end" into a variable as 0, then accidentally it means "from beginning" instead of "from end". 14 | 15 | The benefit of `^x` and `~x` is that they can fully express the "at index length" index as `~0` or `^0`. 16 | 17 | Pro `~`: 18 | * Just works 19 | * No new syntax or types 20 | 21 | Against: 22 | * No protection against bugs (accidentally using `-`) 23 | * Cannot add overloads that go from end 24 | * No documentation for whether the int indexer understands negative 25 | 26 | If we do this just for `Range` (including maybe `^` syntax), then it's hard to add `Index` later. 27 | 28 | Existing types with indexers (e.g. arrays and strings) would be lots of work to evolve to make use of `Index` (or `Range` for that matter). 29 | 30 | Could the `int` conversion to `Index` also take negative numbers, but with the Python meaning? Then it would essentially "subtract 1 from negative numbers". 31 | 32 | The problem with `~` is that it is off-by-one from Python. The `~` can only hide it so much, but if you look in the debugger, `~1` is `-2`! 33 | 34 | Current options: 35 | 36 | 1. Do like Python: just live with "0 from end" not being expressible as a number; it's a special case (5) 37 | 2. Use ~ (1) 38 | 3. Add `Index` and `^` (6) 39 | 4. Don't support "from end" (1) 40 | 41 | Could we disallow "mixed" ranges (one from beginning one from end)? That's not a reasonable restriction. `1..-1` strips off the double quotes, etc. 42 | 43 | We're down to two options, 1 and 3. Let's take these through VB design as well, to see if we get new insights. 44 | 45 | Most likely to care are app developers, like web developers. Library authors will just be trading in Ranges that are already created. 46 | 47 | # Bestest betterness 48 | 49 | 1. When gathering candidates, if one of them has inferred type arguments that do not satisfy constraints, we'll discard the candidate. This means you can overload on constraints! 50 | 2. If you're in a static context, instance members are discarded, and vice versa. Only simple names in instance members will include both, as well as Color/Color situations 51 | 3. Converting a method group to a delegate type, we consider a conversion where the return type is wrong, even though that conversion is an error. Methods with such parameters will also be weeded out. 52 | 53 | In the first round this led to worse error messages (because we no longer pointed to the "wrong" method), but this has been fixed. 54 | 55 | This has been done in a way to not affect type inference. 56 | 57 | 58 | # Fixed arrays -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-12-06.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Dec 6, 2017 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | ## Agenda 6 | 7 | 8 | # Range 9 | 10 | Several arguments against a generic `Range`, such as conversions between them. 11 | 12 | Step() should maybe be step with range 13 | 14 | # Operator `..` 15 | 16 | `op_Range` in IL, marked as special method. 17 | 18 | Should it be target typed? 19 | 20 | Example: `FloatRange`. If we don't supply it, no-one ever can, unless we make it target typed. 21 | 22 | Conversion between ranges does not seem like a good idea: Even though it's technically possible, it might not make conceptual sense. 23 | 24 | Target typing seems better. There's an issue with compat and ambiguity, where operator declarations on the operands and the result may clash. The proposal is to let the target type win. But that may not be necessary. The operator declared in the operands corresponds to the "natural type", and if there's an exact match to the target type, it would win. 25 | 26 | For `IntRange` we get: 27 | 28 | ``` c# 29 | IntRange r = 1 .. 10; 30 | ``` 31 | 32 | Here `..` is defined on the operand types, and when that one is applied, the natural type is `IntRange`, and that wins in overload resolution. 33 | 34 | For `LongRange`: 35 | 36 | ``` c# 37 | LongRange lr = 1 .. 10; 38 | ``` 39 | 40 | That works just dandy with target typing, but if there's also a conversion from `IntRange` to `LongRange` then there seem to be two competing conversions. We probably don't want that, and need to think through if that's the case, and if so how to amend it. 41 | 42 | ## Alternative 1 43 | 44 | Look for an instance method (which can be an extension method), and have declared conversions and no target typing. That would lead to N x N extension methods and N x N conversion methods having to be declared to get the same "convenience". 45 | 46 | But for the situation of having `x .. y` and *not* a target type, it would be simpler, in that it wouldn't need the `..` operator to be retrofitted to existing types. 47 | 48 | ## Alternative 2 49 | 50 | Have a generic `Range`. A range expression produces a range of the best common type of the operands. It can be target typed to another `Range`, as long as there's a conversion from the end points to `S`. Comparison is done by expanding to use of `<=`, which better be defined. 51 | 52 | 53 | 54 | # Wrap around 55 | 56 | Let's make sure the library side doesn't allow foreach to throw on the boundary (`int.MaxValue`) or wrap around infinitely. 57 | 58 | # Inclusive/exclusive 59 | 60 | For floating point/non-enumerable, there's real expressiveness at stake. That's probably not the main scenario, though. 61 | 62 | Problem with exclusive: I create a range from Sunday to Saturday, I need a name for the thing outside the range. Also, what if I need `int.MaxValue` as the top element? 63 | 64 | Problem with inclusive: Going to array.Length. Slicing and windowing. 65 | 66 | Evidence in the Mono code base for instance, shows both prevalent, with a slight overweight of inclusive. 67 | 68 | 69 | 70 | # Range pattern -------------------------------------------------------------------------------- /proposals/csharp-7.2/conditional-ref.md: -------------------------------------------------------------------------------- 1 | # Conditional ref expressions 2 | 3 | The pattern of binding a ref variable to one or another expression conditionally is not currently expressible in C#. 4 | 5 | The typical workaround is to introduce a method like: 6 | 7 | ```csharp 8 | ref T Choice(bool condition, ref T consequence, ref T alternative) 9 | { 10 | if (condition) 11 | { 12 | return ref consequence; 13 | } 14 | else 15 | { 16 | return ref alternative; 17 | } 18 | } 19 | ``` 20 | 21 | Note that this is not an exact replacement of a ternary since all arguments must be evaluated at the call site. 22 | 23 | The following will not work as expected: 24 | 25 | ```csharp 26 | // will crash with NRE because 'arr[0]' will be executed unconditionally 27 | ref var r = ref Choice(arr != null, ref arr[0], ref otherArr[0]); 28 | ``` 29 | 30 | The proposed syntax would look like: 31 | 32 | ```csharp 33 | ? ref : ref ; 34 | ``` 35 | 36 | The above attempt with "Choice" can be _correctly_ written using ref ternary as: 37 | 38 | ```csharp 39 | ref var r = ref (arr != null ? ref arr[0]: ref otherArr[0]); 40 | ``` 41 | 42 | The difference from Choice is that consequence and alternative expressions are accessed in a _truly_ conditional manner, so we do not see a crash if ```arr == null``` 43 | 44 | The ternary ref is just a ternary where both alternative and consequence are refs. It will naturally require that consequence/alternative operands are LValues. 45 | It will also require that consequence and alternative have types that are identity convertible to each other. 46 | 47 | The type of the expression will be computed similarly to the one for the regular ternary. I.E. in a case if consequence and alternative have identity convertible, but different types, the existing type-merging rules will apply. 48 | 49 | Safe-to-return will be assumed conservatively from the conditional operands. If either is unsafe to return the whole thing is unsafe to return. 50 | 51 | Ref ternary is an LValue and as such it can be passed/assigned/returned by reference; 52 | 53 | ```csharp 54 | // pass by reference 55 | foo(ref (arr != null ? ref arr[0]: ref otherArr[0])); 56 | 57 | // return by reference 58 | return ref (arr != null ? ref arr[0]: ref otherArr[0]); 59 | ``` 60 | 61 | Being an LValue, it can also be assigned to. 62 | 63 | ```csharp 64 | // assign to 65 | (arr != null ? ref arr[0]: ref otherArr[0]) = 1; 66 | ``` 67 | 68 | Ref ternary can be used in a regular (not ref) context as well. Although it would not be common since you could as well just use a regular ternary. 69 | 70 | ```csharp 71 | int x = (arr != null ? ref arr[0]: ref otherArr[0]); 72 | ``` 73 | 74 | 75 | ___ 76 | 77 | Implementation notes: 78 | 79 | The complexity of the implementation would seem to be the size of a moderate-to-large bug fix. - I.E not very expensive. 80 | I do not think we need any changes to the syntax or parsing. 81 | There is no effect on metadata or interop. The feature is completely expression based. 82 | No effect on debugging/PDB either 83 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-01-31.md: -------------------------------------------------------------------------------- 1 | # C# Language Design for Jan 31, 2018 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | # Pattern-based fixed 6 | 7 | If a type has a magic method of a certain name, which returns `ref` or `ref readonly`, you can `fixed` the type. 8 | 9 | If you try to pin null or an empty array, you get a null pointer. It never throws. 10 | 11 | For these new types, if it's a reference type we'll check first, and if it's null we'll return a null pointer. If the method wants to signal that there is nothing (e.g. an empty `ImmutableArray` struct), the proposal is to return a null ref. Those don't currently officially exist in the language, but you can manufacture one with unsafe code. 12 | 13 | 14 | ## Extension methods 15 | 16 | There are probably scenarios, such as adding the capability to an existing type based on an existing ref-returning member, or adding to certain constructions of a generic type. 17 | 18 | But even because of consistency with how we've been doing pattern-based things for ages, we should allow it. 19 | 20 | For string we want an instance method to win over the current way, but the current way to win over extension methods. 21 | 22 | 23 | # Ranges 24 | 25 | Terminology: say "bounded/unbounded" instead of "open/closed". "Inclusive/exclusive" for whether the endpoint is included. 26 | 27 | 28 | ## Negative indices 29 | 30 | Proposal to have negative indices: 31 | 32 | ```c# 33 | a[-5..-1] // Starts at length-5, excludes the last element 34 | ``` 35 | 36 | Proposal for start/count syntax: 37 | 38 | ``` c# 39 | a[5..+10] // Starts at 5, goes to 15 (exclusive) 40 | a[-10..+3] // Starts at length-10, goes to length-7 (exclusive) 41 | ``` 42 | 43 | Is it important to support collections with nonzero (positive) start index? 44 | 45 | Should the `Bind` method only take a length, not an index, for instance? Yes. 46 | 47 | There might be the odd collection out there that's indexed, and doesn't start with 0. That's fine, but the feature is not for that. Those types wouldn't have methods that take ranges. 48 | 49 | ## start/length notation 50 | 51 | `0..+-1` 52 | 53 | `-5..+3` // `-5..-2` 54 | `-5..+5` // `-5..` 55 | `-5..+7` // throw 56 | 57 | 58 | ## lexing/syntax 59 | 60 | We've been liking `:` for this, but it is highly ambiguous with many aspects of the language: ternary operator, named arguments, format specifiers in interpolated strings. 61 | 62 | ``` c# 63 | a[3..+7] 64 | a[3.+7] 65 | a[3.:7] // Not ambiguous object o = b ? 3..: 7.: 9 ; 66 | a[3..:7] // object o = b ? 3..: 7..: 9 ; find the range! 67 | a[3:.7] // NO 68 | a[3::7] // NO? 69 | a[3:7] // NO 70 | a[x:y] // NO 71 | ``` 72 | 73 | You don't need elision on either end for this. You only use it if you want to give a length. And if you want to start from the beginning you may just use `..x`: length and end are the same. 74 | 75 | ``` c# 76 | 3..-x 77 | M(.:..) 78 | ``` 79 | 80 | Colons only: ambiguous, and looks symmetric. `::` not as bad, only ambiguous with extern alias; realistic to semantically disambiguate. 81 | Plus: Looks like you are adding something, but clashes with unary + 82 | 83 | `.:` is the plan of record. -------------------------------------------------------------------------------- /meetings/2019/LDM-2019-01-14.md: -------------------------------------------------------------------------------- 1 | 2 | # C# Language Design Notes for January 14th, 2019 3 | 4 | ## Agenda 5 | 6 | 1. Generating null-check for `parameter!` 7 | 8 | ## Discussion 9 | 10 | ### Generating null-check for parameter! 11 | 12 | Proposal: https://github.com/dotnet/csharplang/pull/2144 13 | 14 | *Q: Allow on unconstrained generic parameters?* 15 | 16 | **A**: Not a lot of reason to disallow it. `is` currently doesn't work on 17 | unconstrained type parameters, but we view that as a limitation we'd like to 18 | lift. 19 | 20 | *Q: Where is the null check for constructors?* 21 | 22 | Before or after the base/this calls? Syntactically, it's consistent to leave 23 | it in the constructor body, after the base/this calls. However, this could 24 | cause us to do more work before the exception is eventually thrown. 25 | 26 | **A**: In the case where the base/this call would throw anyway, there's not 27 | much difference. But in the case that the base/this are null-safe, putting 28 | the check in the body just delays the exception, which provides little value. 29 | The null check should be the first statement in the method. In general, the 30 | null check should be performed as soon as possible. 31 | 32 | *Q: Is there a warning for `void M(string? param!)`?* 33 | 34 | `string?` suggests that the method gracefully accepts null, but the feature 35 | immediately throws, which doesn't count. 36 | 37 | However, when overriding you may have a `string?` as your base, but your 38 | override only accepts non-nullable `string`. 39 | 40 | **A**: Yes. In the case where a user is warned about violating the OHI 41 | contract, the correct decision is to silence the OHI warning, keeping 42 | consistency between the implementation and signature of the method. 43 | 44 | *Q: Where is the null check for iterators?* 45 | 46 | **A**: Null check in the stub method, as soon as possible (before the yield). 47 | 48 | *Q: Can you put it on lambda parameter names?* 49 | 50 | **A**: Yes, as long as there are no problems in the syntax. If there 51 | are ambiguities with simple lambda parameter syntax, it can be allowed only 52 | in parenthesized form. 53 | 54 | #### Syntax 55 | 56 | There's a feature in TypeScript where you can put a `!` after a field name 57 | and that field will be exempt from checking that the field is assigned in 58 | the constructor. 59 | 60 | There's concern that `field!` syntax look a lot like `param!` but they would 61 | do almost the opposite thing: `field!` would ignore nulls, while `param!` 62 | would throw on null. 63 | 64 | An alternative would be `void M(string! param)` but it's very strange to put 65 | the `!` next to the type but not be part of the type and not be allowed 66 | almost anywhere else that a type is allowed. 67 | 68 | Options: 69 | 70 | 1. Solve both 71 | 72 | a. `M(string S!)`, `public string! s` 73 | 74 | b. `M(string! S)`, `public string s!` 75 | 1. Solve parameters 76 | 77 | a. `M(string s!)` 78 | 79 | b. `M(string! s)` 80 | 81 | 1. Solve initialization 82 | 83 | a. `public string s!` 84 | 85 | b. `public string! s` 86 | 87 | 1. Do nothing 88 | 89 | **Conclusion** 90 | 91 | Let's try to do (2a), only `void M(string param!)`, as described by Jared. -------------------------------------------------------------------------------- /meetings/README.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Meetings 2 | 3 | C# Language Design Meetings (LDM for short) are meetings by the C# Language Design Team and invited guests to investigate, design and ultimately decide on features to enter the C# language. It is a creative meeting, where active design work happens, not just a decision body. 4 | 5 | Each C# language design meeting is represented by a meeting notes file in this folder. 6 | 7 | ## Purpose of the meetings notes 8 | 9 | Meeting notes serve the triple purposes of 10 | 11 | - recording decisions so they can be acted upon 12 | - communicating our design thinking to the community so we can get feedback on them 13 | - recording rationale so we can return later and see why we did things the way we did 14 | 15 | All have proven extremely useful over time. 16 | 17 | ## Life cycle of meeting notes 18 | 19 | - If upcoming design meetings have a specific agenda, for instance to suit the schedule of visitors, there may be a meeting notes file with that agenda even before the meeting happens. 20 | - Otherwise the meeting agendas are determined just-in-time based on urgency, arrival of new information or ideas, challenges found in design and implementation, and so on. 21 | - After the meeting, notes will be saved directly here. 22 | - Usually they will be raw notes in need of subsequent cleaning up. If that's the case, they will be clearly marked as such, and a [Meeting notes](https://github.com/dotnet/csharplang/labels/Meeting%20notes) work item will track the task of cleaning up the notes. 23 | - When the notes are finalized, a notification is posted as a [discussion issue](https://github.com/dotnet/csharplang/labels/Discussion) to encourage discussion of the decisions made. While quick comments are welcome directly on that issue, it is recommended to open a separate issue for deeper or more topic-specific discussions. 24 | - If the notes impact current proposals, [proposal](https://github.com/dotnet/csharplang/labels/Proposal) work items will track updating those proposals, assigned to their [champions](https://github.com/dotnet/csharplang/labels/Proposal%20champion). 25 | - When updated, the proposals link back to the meetings where the proposal was discussed. 26 | 27 | ## Style of design notes 28 | 29 | The notes serve as the collective voice of the LDM. They cover not just the decisions but the discussion, options and rationale, so that others can follow along in the discussion and provide input to it, and so that we don't forget them for later. 30 | 31 | However, *the notes are not minutes*! They *never* state who said what in the meeting. They will occasionally mention people by name if they are visiting, provided input, should be collaborated with, etc. But the notes aim to represent the shared thinking of the room. If there's disagreement, they will report that, but they won't focus on who wants what. 32 | 33 | This approach is intended to reinforce that the LDMs are a safe space, and a collaborative, creative effort. It is not a negotiation between representatives of different interests. It is not a voting body, and it is not a venue for posturing. Everybody cooperates towards the same end: creating the best language for today's and tomorrow's C# developers. 34 | 35 | -------------------------------------------------------------------------------- /meetings/2016/LDM-2016-10-18.md: -------------------------------------------------------------------------------- 1 | C# Language Design Meeting Notes, Oct 18, 2016 2 | ============================================== 3 | 4 | 5 | ## Agenda 6 | 7 | Go over C# 7.0 features one last (?) time to make sure we feel good about them and address remaining language-level concerns. 8 | 9 | 1. Wildcard syntax 10 | 2. Design "room" between tuples and patterns 11 | 3. Local functions 12 | 4. Digit separators 13 | 5. Throw expressions 14 | 6. Tuple name mismatch warnings 15 | 7. Tuple types in `new` expressions 16 | 17 | 18 | # Pattern matching 19 | 20 | ## Wildcards 21 | If we want to reopen the discussion of using `_`, and it doesn't make C# 7.0, we may find ourselves wanting to block off use of `_` as an ordinary identifier in the new declaration contexts (patterns, deconstruction, out vars). Let's front load the final design of wildcards to decide if anything needs to happen here. 22 | 23 | ## Design "room" between tuples and pattern matching 24 | 25 | `case (int, int) x:` is not currently allowed, in order to leave design space. More specifically we want this to mean a recursive pattern, rather than just a tuple type, once we get to recursive patterns. We're good with leaving this an error in the meantime, even though it may occasionally be puzzling to developers. 26 | 27 | 28 | # Local functions 29 | 30 | We want to ideally allow any expression or set of statements to be lifted out in a local method. There are two ways in which this cannot be realized: 31 | 32 | 1. assignment to readonly fields in constructors - the runtime disallows those assignments if we lift them out to methods. 33 | 2. async methods and iterators - they aren't done executing when they return, so they can't generally contribute to definite assignment of enclosing variables. For async local functions we do recognize assignments that happen before the first await. Is this too subtle? Maybe, but it's fine to keep it. 34 | 35 | 36 | # Digit separators 37 | 38 | We don't allow leading or trailing underbars - they have to be *between* digits: they are digit *separators* after all! We think this is fine, but if we hear feedback to the contrary we can try to relax it later. 39 | 40 | 41 | # Throw expressions 42 | 43 | They are allowed as expression bodies, as the second operand of `??`, and as the second and third operand of `?:`. They are not allowed in `&&` and `||`, and cannot be parenthesized. We think this is a fine place to land. 44 | 45 | 46 | # Tuples 47 | 48 | ## Name mismatch warnings 49 | 50 | We don't currently warn about names moving to a different position. Should we? 51 | 52 | ``` c# 53 | (int first, int last) M() ... 54 | 55 | (int last, int first) t = M(); // Oops! 56 | ``` 57 | 58 | Ideally yes. There are a lot of weird cases that would be hard to track down, though. Let's get the obvious ones. Essentially where we do implicit conversions we would check and warn. 59 | 60 | 61 | ## Use of tuples in `new` 62 | 63 | You cannot `new` up a tuple type. However, we should certainly allow arrays of tuples to be created. It is probably also fine to keep allowing `new`ing of nullable tuple types: 64 | 65 | ``` c# 66 | var array = new (int x, int y)[10]; // Absolutely 67 | var nullable = new (int x, int y)?(); // Why not? 68 | ``` 69 | -------------------------------------------------------------------------------- /proposals/README.md: -------------------------------------------------------------------------------- 1 | # C# Language Proposals 2 | 3 | Language proposals are living documents describing the current thinking about a given language feature. 4 | 5 | Proposals can be either *active*, *inactive*, *rejected* or *done*. *Active* proposals are stored directly in the proposals folder, *inactive* and *rejected* proposals are stored in the [inactive](proposals/inactive) and [rejected](proposals/rejected) subfolders, and *done* proposals are archived in a folder corresponding to the language version they are part of. 6 | 7 | ## Lifetime of a proposal 8 | 9 | A proposal starts its life when the language design team decides that it might make a good addition to the language some day. Typically it will start out being *active*, but if we want to capture an idea without wanting to work on it right now, a proposal can also start out in the *inactive* subfolder. Proposals may even start out directly in the *rejected* state, if we want to make a record of something we don't intend to do. For instance, if a popular and recurring request is not possible to implement, we can capture that as a rejected proposal. 10 | 11 | The proposal may start out as an idea in a [discussion issue](https://github.com/dotnet/csharplang/labels/Discussion), or it may come from discussions in the Language Design Meeting, or arrive from many other fronts. The main thing is that the design team feels that it should be done, and that there's someone who is willing to write it up. Typically a member of the Language Design Team will assign themselves as a champion for the feature, tracked by a [Champion issue](https://github.com/dotnet/csharplang/labels/Proposal%20champion). The champion is responsible for moving the proposal through the design process. 12 | 13 | A proposal is *active* if it is moving forward through design and implementation toward an upcoming release. Once it is completely *done*, i.e. an implementation has been merged into a release and the feature has been specified, it is moved into a subdirectory corresponding to its release. 14 | 15 | If a feature turns out not to be likely to make it into the language at all, e.g. because it proves unfeasible, does not seem to add enough value or just isn't right for the language, it will be [rejected](proposals/rejected). If a feature has reasonable promise but is not currently being prioritized to work on, it may be declared [inactive](proposals/inactive) to avoid cluttering the main folder. It is perfectly fine for work to happen on inactive or rejected proposals, and for them to be resurrected later. The categories are there to reflect current design intent. 16 | 17 | ## Nature of a proposal 18 | 19 | A proposal should follow the [proposal template](proposal-template.md). A good proposal should: 20 | 21 | - Fit with the general spirit and aesthetic of the language. 22 | - Not introduce subtly alternate syntax for existing features. 23 | - Add a lot of value for a clear set of users. 24 | - Not add significantly to the complexity of the language, especially for new users. 25 | 26 | ## Discussion of proposals 27 | 28 | Feedback and discussion happens in [discussion issues](https://github.com/dotnet/csharplang/labels/Discussion). When a new proposal is added to the proposals folder, it should be announced in a discussion issue by the champion or proposal author. 29 | 30 | -------------------------------------------------------------------------------- /proposals/csharp-8.0/ranges.cs: -------------------------------------------------------------------------------- 1 | namespace System 2 | { 3 | public readonly struct Index 4 | { 5 | private readonly int _value; 6 | 7 | public int Value => _value < 0 ? ~_value : _value; 8 | public bool FromEnd => _value < 0; 9 | 10 | public Index(int value, bool fromEnd) 11 | { 12 | if (value < 0) throw new ArgumentException("Index must not be negative.", nameof(value)); 13 | 14 | _value = fromEnd ? ~value : value; 15 | } 16 | 17 | public static implicit operator Index(int value) 18 | => new Index(value, fromEnd: false); 19 | } 20 | 21 | public readonly struct Range 22 | { 23 | public Index Start { get; } 24 | public Index End { get; } 25 | 26 | private Range(Index start, Index end) 27 | { 28 | this.Start = start; 29 | this.End = end; 30 | } 31 | 32 | public static Range Create(Index start, Index end) => new Range(start, end); 33 | public static Range FromStart(Index start) => new Range(start, new Index(0, fromEnd: true)); 34 | public static Range ToEnd(Index end) => new Range(new Index(0, fromEnd: false), end); 35 | public static Range All() => new Range(new Index(0, fromEnd: false), new Index(0, fromEnd: true)); 36 | } 37 | 38 | static class Extensions 39 | { 40 | public static int get_IndexerExtension(this int[] array, Index index) => 41 | index.FromEnd ? array[array.Length - index.Value] : array[index.Value]; 42 | 43 | public static int get_IndexerExtension(this Span span, Index index) => 44 | index.FromEnd ? span[span.Length - index.Value] : span[index.Value]; 45 | 46 | public static char get_IndexerExtension(this string s, Index index) => 47 | index.FromEnd ? s[s.Length - index.Value] : s[index.Value]; 48 | 49 | public static Span get_IndexerExtension(this int[] array, Range range) => 50 | array.Slice(range); 51 | 52 | public static Span get_IndexerExtension(this Span span, Range range) => 53 | span.Slice(range); 54 | 55 | public static string get_IndexerExtension(this string s, Range range) => 56 | s.Substring(range); 57 | 58 | public static Span Slice(this T[] array, Range range) 59 | => array.AsSpan().Slice(range); 60 | 61 | public static Span Slice(this Span span, Range range) 62 | { 63 | var (start, length) = GetStartAndLength(range, span.Length); 64 | return span.Slice(start, length); 65 | } 66 | 67 | public static string Substring(this string s, Range range) 68 | { 69 | var (start, length) = GetStartAndLength(range, s.Length); 70 | return s.Substring(start, length); 71 | } 72 | 73 | private static (int start, int length) GetStartAndLength(Range range, int entityLength) 74 | { 75 | var start = range.Start.FromEnd ? entityLength - range.Start.Value : range.Start.Value; 76 | var end = range.End.FromEnd ? entityLength - range.End.Value : range.End.Value; 77 | var length = end - start; 78 | 79 | return (start, length); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-04-19.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Apr 19, 2017 2 | 3 | *Quote of the day*: "I'm not thrilled with the decision, but it was a constrained call" 4 | 5 | ## Agenda 6 | 7 | 1. Improved best common type 8 | 2. Diamonds with classes 9 | 3. Structs and default implementations 10 | 4. Base invocation 11 | 12 | 13 | # Improved best common type 14 | 15 | ``` c# 16 | b ? null : 7 // should be int? not error 17 | ``` 18 | 19 | This is a small, isolated change that leads to a nice improvement. Let's do it! 20 | 21 | 22 | # Diamonds with classes 23 | 24 | A class implementation of an interface member should always win over a default implementation in an interface, even if it is inherited from a base class. Default implementations are always a fallback only for when the class does not have any implementation of the member at all. 25 | 26 | 27 | # Structs and default implementations 28 | 29 | It is very hard to use default implementations from structs without boxing. In general, the only way to use interface methods on a struct without copying or boxing is through generics and ref parameters: 30 | 31 | ``` c# 32 | public static void Increment(ref T t) where T : I => t.Increment(); 33 | ``` 34 | 35 | But for default implementations, even that won't necessarily avoid boxing! What is the type of `this` in the default implementation of `I.Increment`? If it's `I`, then `this` is a boxed form of the struct! We can avoid that by having an implicit type parameter, a "this type", which is constrained by `I`. But then, what is the name of that implicit type parameter in the body? If it doesn't have a name, you can't use it in the body. If it does have a name, what is the syntax for that? 36 | 37 | For inherited members on structs today, they do in fact get boxed, but it is never observable - because the behavior of those three methods (`ToString`, etc.) doesn't mutate, or otherwise reveal the boxing. 38 | 39 | What can we do? 40 | 1. *Forbid structs to make use of default implementations*: Can't do that. Now adding a new interface member with a default implementation would break implementing structs. 41 | 2. *Come up with some code gen strategy like implicit this types*: Pushes the problem elsewhere, at a high cost and with a high risk that you box later anyway. 42 | 3. *Don't worry about it, and leave it as a wart in the language*: It looks like a cop out, but there really doesn't seem to be a good alternative. C# already has places where structs and interfaces behave weirdly together; this just adds to the pile. 43 | 44 | ## Conclusion 45 | 46 | Let's stick with option 3. It would be a breaking change to do 2 later, so we can never change it. 47 | 48 | 49 | # Base invocation 50 | 51 | What should be the syntax for base invocation? Some candidates: 52 | 53 | ``` c# 54 | I.base.M() 55 | base(I).M() 56 | base.M() 57 | ``` 58 | 59 | `base(I)` is the winner. It reads like `default(T)`, and seems the best fit. 60 | 61 | Should these be permitted in classes too, to specify base interface implementations? Yes. Otherwise the diamond problem isn't solved. 62 | 63 | We could actually expand it to work *on* classes too, to allow a more distant base implementation to be called. Let's decide that later. 64 | 65 | In an interface, can I say `base(IA)` that isn't a *direct* base interface? Yes. 66 | 67 | In a class, can I say `base(IA)` that isn't a *direct* base interface? Yes. -------------------------------------------------------------------------------- /proposals/csharp-7.1/target-typed-default.md: -------------------------------------------------------------------------------- 1 | # Target-typed "default" literal 2 | 3 | * [x] Proposed 4 | * [x] Prototype 5 | * [x] Implementation 6 | * [ ] Specification 7 | 8 | ## Summary 9 | [summary]: #summary 10 | 11 | The target-typed `default` feature is a shorter form variation of the `default(T)` operator, which allows the type to be omitted. Its type is inferred by target-typing instead. Aside from that, it behaves like `default(T)`. 12 | 13 | ## Motivation 14 | [motivation]: #motivation 15 | 16 | The main motivation is to avoid typing redundant information. 17 | 18 | For instance, when invoking `void Method(ImmutableArray array)`, the *default* literal allows `M(default)` in place of `M(default(ImmutableArray))`. 19 | 20 | This is applicable in a number of scenarios, such as: 21 | 22 | - declaring locals (`ImmutableArray x = default;`) 23 | - ternary operations (`var x = flag ? default : ImmutableArray.Empty;`) 24 | - returning in methods and lambdas (`return default;`) 25 | - declaring default values for optional parameters (`void Method(ImmutableArray arrayOpt = default)`) 26 | - including default values in array creation expressions (`var x = new[] { default, ImmutableArray.Create(y) };`) 27 | 28 | 29 | ## Detailed design 30 | [design]: #detailed-design 31 | 32 | A new expression is introduced, the *default* literal. An expression with this classification can be implicitly converted to any type, by a *default-or-null literal conversion*. 33 | 34 | The inference of the type for the *default* literal works the same as that for the *null* literal, except that any type is allowed (not just reference types). 35 | 36 | This conversion produces the default value of the inferred type. 37 | 38 | The *default* literal may have a constant value, depending on the inferred type. So `const int x = default;` is legal, but `const int? y = default;` is not. 39 | 40 | The *default* literal can be the operand of equality operators, as long as the other operand has a type. So `default == x` and `x == default` are valid expressions, but `default == default` is illegal. 41 | 42 | ## Drawbacks 43 | [drawbacks]: #drawbacks 44 | 45 | A minor drawback is that *default* literal can be used in place of *null* literal in most contexts. Two of the exceptions are `throw null;` and `null == null`, which are allowed for the *null* literal, but not the *default* literal. 46 | 47 | ## Alternatives 48 | [alternatives]: #alternatives 49 | 50 | There are a couple of alternatives to consider: 51 | 52 | - The status quo: The feature is not justified on its own merits and developers continue to use the default operator with an explicit type. 53 | - Extending the null literal: This is the VB approach with `Nothing`. We could allow `int x = null;`. 54 | 55 | ## Unresolved questions 56 | [unresolved]: #unresolved-questions 57 | 58 | - [x] Should *default* be allowed as the operand of the *is* or *as* operators? Answer: disallow `default is T`, allow `x is default`, allow `default as RefType` (with always-null warning) 59 | 60 | ## Design meetings 61 | 62 | - [LDM 3/7/2017](https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-03-07.md) 63 | - [LDM 3/28/2017](https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-03-28.md) 64 | - [LDM 5/31/2017](https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-05-31.md#default-in-operators) 65 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-08-28.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Aug 28, 2017 2 | 3 | 4 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 5 | 6 | ## Agenda 7 | 8 | 9 | # Ref-like types safety rules 10 | 11 | https://github.com/dotnet/csharplang/blob/master/proposals/span-safety.md#draft-language-specification 12 | 13 | The need for ref-like types quickly arises, when you need to flow more than one span, etc. 14 | 15 | The fact that span is array like is not very interesting for the safety rules. The interesting thing is that it embeds a reference. Indexing is not a core concern in the general case. 16 | 17 | Once you can embed refs in struct, the question: How can you assign them? 18 | 19 | With assignment, you can now assign to ref parameters of ref-like structs, so every assignment is a potential return. 20 | 21 | "Let's not refer to local data" does not work here: 22 | 23 | - we *want* the feature to refer to local data. stackalloc, etc. 24 | - lightweight params, etc. 25 | - we want to protect our future ability to allow this even if we could live with it now 26 | 27 | So: We need to allow local data inside ref-like structs. 28 | 29 | We tie a scope to variables holding ref-like structs. That scope is based on what the variable is initialized with. We call this the "escape level". 30 | 31 | Essentially, on the assignment we check that the assigned-to variable's scope is no wider than the assigned value's scope. 32 | 33 | The rules assume that everyone plays by them. 34 | 35 | 36 | Special craziness for multiple arguments, some which are refs, some which are refs to ref-like: 37 | 38 | ``` c# 39 | void M(ref Span x, ref int y) 40 | { 41 | // One of two things must be true about this: 42 | // 1. This is an error 43 | // 2. The caller has guaranteed this is safe 44 | x = new Span(ref y); 45 | } 46 | ``` 47 | We need to do this on the caller, who knows most about the situation. The caller therefore has to assume that there is cross-assignment in the callee. 48 | 49 | 50 | Q: Could you track the escape level during flow analysis, changing it locally? It's possible we could, but it seems extremely complex. We propose strict rules now; a flow analysis later would be more permissive, if we can figure it out. That's an important property! 51 | 52 | 53 | "ref safe to escape" is how far you can escape the variable. "safe to escape" is how far you can escape a value. Only relevant if that value is a ref-like struct that can contain a ref. 54 | 55 | Quality of error messages: We'll do a decent job, but a) it rarely gets complicated/cascading, b) we can improve them later if they turn out not to be good enough. Rust has error messages about this kind of thing that are just stellar, but this is bread-and-butter code in Rust, whereas it's exceptional here. 56 | 57 | Defer: Should uninitialized ref-struct locals be allowed? 58 | Defer: Should default values of ref-struct types be allowed? 59 | 60 | 61 | 62 | Methods can return by-ref, ref-like and by-ref ref-like! We say that by-ref ref-like parameters are not returnable! 63 | 64 | Open issue with ref dynamic: Treat them like in parameters, in how we deal with compiler-generated temps. 65 | 66 | 67 | Language restrictions: 68 | 69 | - Local functions - it's a shame that we can't factor out this stuff to local functions, because of the closure rule. But we can fix it later if necessary. 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /meetings/2015/LDM-2015-08-18.md: -------------------------------------------------------------------------------- 1 | # C# Design Notes for Aug 18, 2015 2 | 3 | Discussion for these notes can be found at https://github.com/dotnet/roslyn/issues/5033. 4 | 5 | ## Agenda 6 | 7 | A summary of the design we (roughly) landed on in #5031 was put out on GitHub as #5032, and this meeting further discussed it. 8 | 9 | 1. Array creation 10 | 2. Null checking operator 11 | 3. Generics 12 | 13 | # Array creation with non-nullable types 14 | 15 | For array creation there is the question whether to allow (big hole) or disallow (big nuisance?) on non-nullable reference types. We'll leave it at allow for now, but may reconsider. 16 | 17 | # Null checking operator 18 | 19 | Casting to a non-nullable reference type would not, and should not, do a runtime null check. Should we, however, have an *operator* for checking null, throwing if the value *is* null, resulting in the non-null value if it isn't? 20 | 21 | This seems like a good idea. The operator is postfix `!`, and it should in fact apply to values of nullable *value* types as well as reference types. It "upgrades" the value to non-nullable, by throwing if it is null. 22 | 23 | ``` c# 24 | if (person.Kind == Student) 25 | { 26 | List courses = person.Courses!; // I know it's not null for a student, but the compiler doesn't. 27 | ... 28 | } 29 | ``` 30 | 31 | The `!` operator naturally leads to `x!.y`, which is great! Although `!.` is two operators, it will feel as a cousin of `?.` (which is one operator). While the latter is conditional on null, the former just plows through. Naively, it implies two redundant null checks, one by `!` and one by `.`, but we'll optimize that of course. 32 | 33 | ``` c# 34 | if (person.Kind == Student) 35 | { 36 | var passed = !person.Courses!.Any(c => c.Grade == F); 37 | ... 38 | } 39 | ``` 40 | 41 | Technically this would allow `x!?.y`, which comes quite close to swearing. We should consider warning when you use `?.` on non-null things. 42 | 43 | VB may have a problem with post-fix `!`. We'll cross that bridge when we get there. 44 | 45 | 46 | # Generics and nullability 47 | 48 | Is it too heavyhanded to require `?` on constraints to allow nullable type arguments? 49 | 50 | Often, when you have a constraint it is because you want to operate on instances. So it's probably good that the default is not nullable. 51 | 52 | It may feel a bit egregious to require it on *all* the constraints of a type parameter, though. Should we put any `?`'s on the type parameter declaration instead of in the constraints? No, that is too weird and different. The case of multiple nullable constraints is probably sufficiently rare that it is reasonable to ask folks to put a `?` on each. In fact we should disallow having `?` on only some, since those question marks won't have an effect: they'll be cancelled by the non-nullable fellow constraints. 53 | 54 | The proposal talks about allowing `?` on the use of type parameters to explicitly override their nullness. Maybe we should have an explicit `!` as well, to explicitly override in the other direction: non-nullable. Think for instance of a `FirstNonNull` method. 55 | 56 | ``` c# 57 | T! FirstNonNull(IList list) { ... } 58 | T? FirstOrDefault(IList list) { ... } 59 | ``` 60 | 61 | This means complexity slowly creeps into the proposal, thanks to generics. However, it seems those overrides are relatively rare, yet really useful when you need them. 62 | 63 | `T!` would only be allowed on type parameters, and only when they are not already non-null by constraint. 64 | 65 | 66 | -------------------------------------------------------------------------------- /proposals/csharp-7.1/infer-tuple-names.md: -------------------------------------------------------------------------------- 1 | # Infer tuple names (aka. tuple projection initializers) 2 | 3 | ## Summary 4 | [summary]: #summary 5 | 6 | In a number of common cases, this feature allows the tuple element names to be omitted and instead be inferred. For instance, instead of typing `(f1: x.f1, f2: x?.f2)`, the element names "f1" and "f2" can be inferred from `(x.f1, x?.f2)`. 7 | 8 | This parallels the behavior of anonymous types, which allow inferring member names during creation. For instance, `new { x.f1, y?.f2 }` declares members "f1" and "f2". 9 | 10 | This is particularly handy when using tuples in LINQ: 11 | 12 | ```csharp 13 | // "c" and "result" have element names "f1" and "f2" 14 | var result = list.Select(c => (c.f1, c.f2)).Where(t => t.f2 == 1); 15 | ``` 16 | 17 | ## Detailed design 18 | [design]: #detailed-design 19 | 20 | There are two parts to the change: 21 | 22 | 1. Try to infer a candidate name for each tuple element which does not have an explicit name: 23 | - Using same rules as name inference for anonymous types. 24 | - In C#, this allows three cases: `y` (identifier), `x.y` (simple member access) and `x?.y` (conditional access). 25 | - In VB, this allows for additional cases, such as `x.y()`. 26 | - Rejecting reserved tuple names (case-sensitive in C#, case-insensitive in VB), as they are either forbidden or already implicit. For instance, such as `ItemN`, `Rest`, and `ToString`. 27 | - If any candidate names are duplicates (case-sensitive in C#, case-insensitive in VB) within the entire tuple, we drop those candidates, 28 | 2. During conversions (which check and warn about dropping names from tuple literals), inferred names would not produce any warnings. This avoids breaking existing tuple code. 29 | 30 | Note that the rule for handling duplicates is different than that for anonymous types. For instance, `new { x.f1, x.f1 }` produces an error, but `(x.f1, x.f1)` would still be allowed (just without any inferred names). This avoids breaking existing tuple code. 31 | 32 | For consistency, the same would apply to tuples produced by deconstruction-assignments (in C#): 33 | 34 | ```csharp 35 | // tuple has element names "f1" and "f2" 36 | var tuple = ((x.f1, x?.f2) = (1, 2)); 37 | ``` 38 | 39 | The same would also apply to VB tuples, using the VB-specific rules for inferring name from expression and case-insensitive name comparisons. 40 | 41 | When using the C# 7.1 compiler (or later) with language version "7.0", the element names will be inferred (despite the feature not being available), but there will be a use-site error for trying to access them. This will limit additions of new code that would later face the compatibility issue (described below). 42 | 43 | ## Drawbacks 44 | [drawbacks]: #drawbacks 45 | 46 | The main drawback is that this introduces a compatibility break from C# 7.0: 47 | 48 | ```csharp 49 | Action y = () => M(); 50 | var t = (x: x, y); 51 | t.y(); // this might have previously picked up an extension method called “y”, but would now call the lambda. 52 | ``` 53 | 54 | The compatibility council found this break acceptable, given that it is limited and the time window since tuples shipped (in C# 7.0) is short. 55 | 56 | ## References 57 | - [LDM April 4th 2017](https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-04-05.md#tuple-names) 58 | - [Github discussion](https://github.com/dotnet/csharplang/issues/370) (thanks @alrz for bringing this issue up) 59 | - [Tuples design](https://github.com/dotnet/roslyn/blob/master/docs/features/tuples.md) 60 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-11-08.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Nov 8, 2017 2 | 3 | ## Agenda 4 | 5 | We went over the status of the prototype for nullable reference types, to address outstanding questions and make any last minute calls before release. 6 | 7 | 1. Constructors 8 | 2. Dotted names 9 | 3. Default expressions 10 | 4. Should we track null state for nonnullable ref types? 11 | 5. Inferred types for method type inference 12 | 6. Inferred nullability in hover tips 13 | 7. Smaller things not yet done 14 | 8. Unconstrained generics 15 | 9. Other issues 16 | 17 | 18 | # Constructors 19 | 20 | Currently the prototype warns on a constructor that doesn't directly initialize all fields, even if it has a `this(...)` constructor initializer. It should exempt such constructors completely, since it will have required the called constructors to fully initialize. 21 | 22 | We may consider a more nifty analysis than that later, at least for private constructors, but it doesn't seem high priority for the prototype. 23 | 24 | It's a warning per constructor. If it's on the implicit (default) constructor it goes on the class name. That's good. 25 | 26 | We don't warn for the default constructor on structs. It's not really actionable, we think, since people can't write their own default constructor or add initializers to fields of structs. But this is worth revisiting later, probably in the context of Xtreme mode. 27 | 28 | 29 | # Dotted names 30 | 31 | Now work in the prototype, modulo a few bugs. (Not fully handling reassignment). 32 | 33 | 34 | # Default expressions 35 | 36 | Should `default(string)` be of type `string?` or should it yield a warning *in and of itself*, and be of the type `string` (the type it states). 37 | 38 | This is a question that's related to whether `string s = null` as a local declaration yields a warning. 39 | 40 | We're going to leave the current implementation, which makes `default(string)` be a `string?`. 41 | 42 | Similar with `(string)null`. It's type is `string?`. 43 | 44 | 45 | # Should we track null state for nonnullable ref types? 46 | 47 | Not now. Worth thinking about for later, as a stop gap. 48 | 49 | 50 | # Inferred types for method type inference 51 | 52 | In the current implementation, the best common type and method type inference don't pick up *inferred* nullability, but only *declared* nullability. 53 | 54 | ``` c# 55 | void M(string? s) 56 | { 57 | if (s == null) return; 58 | var a = new[] { s }; // string?[], should be string[] 59 | } 60 | ``` 61 | 62 | We can live with that as a known issue in the prototype. 63 | 64 | 65 | # Inferred nullability in hover tips 66 | 67 | Currently shows declared nullability. Relatively big work item, won't be fixed in prototype. So we need to set expectations. 68 | 69 | 70 | # Smaller things not yet done 71 | 72 | Some warnings not given. That's alright, we'll give more in the future. 73 | New constraints (`class?`, `object`) aren't added. That's not blocking. 74 | Variance: we'll deal with it when we get there. 75 | 76 | 77 | # Unconstrained generics 78 | 79 | Need to revisit to see if the weirdness we do is the right weirdness. Based on design review feedback, it is ok if what we do is not consistent with the rest of the language; the higher order bit is that it is helpful, intuitive and not obnoxious. 80 | 81 | 82 | # Other issues 83 | 84 | - No switch in the prototype; warnings are always on. We need to design the command line switch. 85 | - Annotations for TryGet etc. still need to be designed. 86 | - How to update BCL with annotations. (Automatically?) 87 | 88 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-03-21.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Mar 21, 2018 2 | 3 | ***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!*** 4 | 5 | ## Agenda 6 | 7 | We discussed various open questions around nullable reference types 8 | 9 | 10 | # The null-forgiving operator 11 | 12 | ## Name 13 | 14 | Null-acquiescing 15 | Null-forgiving 16 | Null-silencing 17 | Null-quieting 18 | Darnit 19 | 20 | ## What does it do? 21 | 22 | Changes top-level nullability to not-null, as well as silencing warnings even on nested nullability 23 | 24 | ``` c# 25 | void M(string? ns, List? ln) 26 | { 27 | string s = ns!; // silence and change nullability 28 | var s2 = ns!; // silence and produce string 29 | ln = null; 30 | List? l = ln!; // silence and change 31 | var l2 = ln!; 32 | // Idea 1 33 | l! // change nullability at the top level 34 | l // idea: change nullability at the nested level 35 | // Idea 2 36 | (List?!)ln; // I do a cast that should warn but ! silences it 37 | List? l3 = (!)ln; // Shorthand 38 | } 39 | ``` 40 | 41 | Should `!` only change top-level nullability, and not suppress nested diagnostics? Or the other way around? Is it a problem that it does both and "suppresses too much". 42 | 43 | Since we made affordances for legacy code, the need for passing `null!` for instance is less: unannotated APIs already suppress warnings. 44 | 45 | Scenarios for `!` now that legacy APIs aren't a scenario: 46 | 47 | 1. I'm in the middle of converting my own code; need them at the boundary 48 | 2. The compiler can't figure it out, but I'm right 49 | 50 | Great example of the latter: 51 | 52 | ``` c# 53 | List strings; 54 | var query = strings.Where(s => s != null).Select(s => s.Length); // What?? Why warning? 55 | ``` 56 | Here you can put the `!` in `s!.Length`, but what if you have 57 | 58 | ``` c# 59 | void M(IEnumerable source) { ... } 60 | List strings; 61 | M(strings.Where(s => s != null)); // What?? Why warning? 62 | ``` 63 | 64 | Here you need to suppress on the nested nullability, because you know better. 65 | 66 | Currently you can say `!` at the end of the argument to set it right. Should that be a different syntax? 67 | 68 | What's the problem with `!` doing both jobs? 69 | 70 | ``` c# 71 | void M(string? ns, List? ln) 72 | { 73 | // I happen to know that if ln is not null, it doesn't contain nulls 74 | List? l = ln!; 75 | _ = l.Count; // warning? 76 | } 77 | ``` 78 | 79 | Now `l.Length` is not warned on because `!` did "too much". We wanted it to silence warnings, but it also changed the null state. 80 | 81 | The simplest fix with current semantics would be to cast to a nullable type: 82 | 83 | ``` c# 84 | List? l = (List?)ln!; // or 85 | var l = (List?)ln!; 86 | ``` 87 | 88 | Proposals: 89 | 90 | 1. Change null state and suppress 4 91 | 2. Two syntaxes 5 92 | 3. Only suppress warnings 1 93 | 4. Only change null-state 0 94 | 95 | Approachable and easy to use. They are all about telling the compiler to shut up when you know what you're doing. 96 | 97 | For 3, the things we would make people do to change the null state would also be a hurdle. 98 | 99 | If we separate it, one may be a temporary feature that will go away over time, once people have transitioned. 100 | 101 | We're not going to settle this today. The prototype does "1+" which is even more lenient than 1. 102 | 103 | 104 | 105 | # Special methods 106 | 107 | # Hidden diagnostic -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-03-29.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Mar 29, 2017 2 | 3 | ## Agenda 4 | 5 | 1. Nullable scenarios 6 | 2. `Span` safety 7 | 8 | # Nullable scenarios 9 | 10 | Identify all the scenarios where nullable needs to be right. 11 | 12 | Philosophical debate about what the feature is. Should it be as strict as possible, or more forgiving? Is it more a type feature or more a helpful warning feature? 13 | 14 | We've made decisions about where we are on that spectrum. Do we trust that as a starting point, or do we want to reexamine those at this point? 15 | 16 | Example: We want to track variables with flow analysis. for simple names `x` (local variables, parameters) that has reasonably high fidelity. As we move to `x.y.z` the confidence goes down, but the usefulness goes up. 17 | 18 | Let's agree right now that we're pretty far from the perfect guarantee. It's not that there is anything fundamentally wrong with that, there's just no way to fit it. Let's make lemonade. 19 | 20 | We have design choices and we have interesting scenarios. Two kinds: 21 | - Here are situations where people get into problems with nulls; look at whether the feature solves it 22 | - Understand false positives and negatives too: getting errors that incorrectly flag a "problem", vs not being told about a problem. 23 | 24 | False alarms are a problem, because they will discourage people from taking on the feature, and from taking the reports seriously. We can't be too strict. 25 | 26 | Also an issue of writing the libraries: is this sufficiently expressible? 27 | 28 | Another measure of success: How much does a client of a newly annotated old library have to change in order to accommodate? 29 | 30 | Example: If we are going to say that you annotate your locals, that might be more consistent with the rest of the language, but might also cause more code to need to be changed. 31 | 32 | This may be a question of gradual adoption strategies. It could be that you start out consuming nullables (which are the new thing, syntactically), without making unannotated mean "non-null". 33 | 34 | Generics is always a fun situation. Are they in subtyping relationship? That informs variance. And so on. 35 | 36 | Next steps: Get some samples going. 37 | 38 | 39 | # `Span` safety 40 | 41 | `Span` is special: it logically contains a `ref` and a size/range. Can't be allowed to tear, and may reference things on the stack: Therefore can't live on the heap. Also, needs to respect the lifetime of a local to which it has a ref. We'll introduce a notion of "ref-like" types, in anticipation of more of these. 42 | 43 | Other "ref-like" types already exist (e.g. `TypedReference`), but are pretty obscure and unsafe. We won't let our design influence by them, but might opportunistically unify. Also their implementation in the compiler can serve as a checklist for dealing with the new notion of ref-like types. 44 | 45 | Declaration: need something in metadata. Old compilers will ignore it. We cannot poison a type with modreq's. We could `Obsolete` the type, and the new compiler could recognize it somehow. 46 | 47 | For now we shouldn't support language-level declaration of ref-like types. There's a whole set of rules we'd need to figure out for such declarations; instead we are good with just recognizing this in metadata and acting accordingly for now. 48 | 49 | ref-like types cannot be passed by ref, only by `in`, Therefore they must be readonly structs, so that `this` is not passed by ref. 50 | 51 | In general, these rules are quite restrictive in certain ways. Let's try it and see if it works in our scenarios. There may be an `unsafe` opt-out, or we may nuance the rules. 52 | 53 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-12-05.md: -------------------------------------------------------------------------------- 1 | 2 | # C# LDM notes for Dec 5, 2018 3 | 4 | ## Agenda 5 | 6 | Tracked nullable states, their correspondence to source and the rules they follow in flow analysis 7 | 8 | ## Proposal 9 | 10 | Since we are treating nullable analysis as a conventional lattice data flow analysis, 11 | we'd like to define the lattice. 12 | 13 | Here are the states based on current implementation: 14 | 15 | * Unknown 16 | * NotAnnotated 17 | * Annotated 18 | * NotNullable 19 | * Nullable 20 | 21 | The goal is to get to a concrete specification that defines how the flow state 22 | affects the language and how flow states interact. 23 | 24 | The first task is to decide how the annotations and the nullable context 25 | interact to define the flow state. At the moment, the context of where the 26 | variable is *defined* decides what the state of the variable is in, when it 27 | is used in a method. However, there is a proposal to use the context *of the 28 | method* to define the state the variable is in. 29 | 30 | There are now new proposed states, which are now split between states used in 31 | flow analysis, and states used as semantic annotation. 32 | 33 | Semantic states: 34 | 35 | * Nullable 36 | * Oblivious 37 | * NotAnnotated 38 | 39 | Flow states: 40 | 41 | * "Can be null" (true or false) 42 | 43 | Consequences: 44 | 45 | When reading a field we compute the flow state as follows: 46 | 47 | * `Nullable` -> `true` 48 | * `Oblivious` -> `false` 49 | * `NotAnnotated` -> `true` only if a type parameter that could be nullable, 50 | else `false` 51 | 52 | When writing a field we warn as follows: 53 | 54 | * `NotAnnotated`, `true` -> warn if the RHS can be null when the variable (or 55 | type) can't (i.e., take into account both being related type parameters) 56 | 57 | ## Discussion 58 | 59 | *Q: What warnings do we produce for the following?* 60 | 61 | ```C# 62 | void M(T p1) where T : class? 63 | { 64 | p1 = p1; // p1 may be null, but we don't produce a warning because 65 | // the T could be nullable 66 | } 67 | ``` 68 | 69 | The consequence is: 70 | 71 | ```C# 72 | void M(T p) where T : class? 73 | { 74 | p = null; // This DOES produce a warning, because there's a conversion 75 | // from `null` to T and we don't know that T is nullable 76 | } 77 | ``` 78 | 79 | Also: 80 | 81 | ```C# 82 | void M(T p) where T : class? 83 | { 84 | if (p == null) // <- this is fine 85 | { 86 | T x = p; // <- this also doesn't give a warning because 87 | // there is no conversion 88 | } 89 | } 90 | ``` 91 | 92 | And: 93 | 94 | ```C# 95 | void M(string p1) 96 | { 97 | if (p1 == null) 98 | { 99 | string x = p1; // W warning? -> Yes 100 | } 101 | } 102 | ``` 103 | 104 | 105 | Between the current state and the new proposal, what's the behavior of the following? 106 | 107 | ```C# 108 | #nullable disable 109 | var t = M(new object()); // What is T inferred as? 110 | ... 111 | #nullable enable 112 | t.Value = null; // warning? 113 | ... 114 | 115 | Box M(T t) => new Box(t); 116 | ``` 117 | 118 | Current: `Box` is inferred as `Box`, warning 119 | New proposal: `Box` is inferred as `Box`, so no warning 120 | 121 | 122 | ```C# 123 | void M(T x) 124 | { 125 | if (x == null) throw; 126 | var y = x; 127 | var z = M(y); // What is the inferred type? 128 | z.Value = null; // warning? 129 | } 130 | ... 131 | Box M(T t) => new Box(t); 132 | ``` 133 | 134 | Current implementation: `Box` is `Box`, warning 135 | New proposal: `Box` is inferred as `Box`, no warnings 136 | -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-05-26.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for May 26, 2017 2 | 3 | ## Agenda 4 | 5 | 1. Native ints 6 | 7 | # Native ints 8 | 9 | We would like to supply high-quality native-size integers. The best we have today is `IntPtr`, which lacks most operators and have a few behavioral weaknesses, including a suboptimal `ToString` implementation. Xamarin has introduced user-defined `nint` and `nuint` types for interop, but those can't completely do the trick; for instance, user-defined operators cannot distinguish between checked and unchecked contexts. 10 | 11 | Options: 12 | 13 | 1. Improve `IntPtr` with operators 14 | 2. Add `nint` and `nuint` as a user defined struct (the Xamarin approach) 15 | 3. Add `nint` and `nuint` to the language 16 | 1. compile down to `IntPtr` and add an attribute to persist the additional type info in metadata 17 | 2. project language-level types to new user defined structs 18 | 4. A combo of 1 and 3 19 | 20 | `IntPtr` has a few operators today; for instance `+` with `int` (which is checked on 32 bit and unchecked on 64 bit!), and some conversions. 21 | 22 | The new structs in 3.2 look something like this: 23 | 24 | ``` c# 25 | struct NativeInt 26 | { 27 | public IntPtr Value; 28 | public override string ToString() { ... } 29 | } 30 | /// etc 31 | ``` 32 | 33 | But operators are implemented by the language, not as user-defined operators. 34 | 35 | The difference between 3.1 and 3.2 is that with 3.2 at runtime we have different types, so we can have differentiated runtime behavior: `ToString` and reflection can tell them apart. 36 | 37 | A downside is that operations that take `ref IntPtr` (like `Interlocked.CompareExchange`) wouldn't automatically take `ref nint`. Having the public mutable field would let things still work for people, and we could go through and add `nint` overloads over time to make it better. 38 | 39 | This should be in mscorlib, but that takes time. Is there anything we can do to mitigate in the meantime? We could ship a nuget package etc, but there's some cost to that, including indefinite maintenance. But some of the people who would benefit from this will be in a terrible spot if we don't provide something. 40 | 41 | We also need to deal with native float. There is no option to do 3.1 for floats; there is no `IntPtr` equivalent. So that one would need a framework type. However, we could probably live with that `nfloat` struct moving into the frameworks over time - other than Xamarin, which would add it faster for its interop scenarios. 42 | 43 | With 3.1, if you consume a `nint`-attributed `IntPtr` with an old compiler, would it treat it as an `intPtr`? If that's the case then the code would subtly change behavior on compiler upgrade. Unfortunate! We could perhaps poison `nint` with `ModReq` so that they cannot be consumed by existing compilers, but now `nint` really *is* a different type, and requires separate overloads of methods that take it as a parameter. 44 | 45 | Another option is to obsolete the user-defined operators on `IntPtr`, to drive people to use `nint` instead. 46 | 47 | ## Objections to 3.2: 48 | - Adoption, where a separate struct would take a while to propagate (we feel we've mostly mitigated this) 49 | - We'll emit slightly less efficient and more verbose IL in a couple of cases 50 | - Needing new overloads for `nint` where there are `IntPtr` overloads today (or at least a conversion, and new overloads where there are `ref IntPtr` parameters). 51 | 52 | Objection to 3.1: 53 | - No runtime distinction (reflection and `ToString`) 54 | - `ToString` happens all the time 55 | 56 | ## Conclusion 57 | We're torn, and evenly balanced on preferring 3.1 vs 3.2. Could maybe be convinced to 3.2 if we can solve how do existing users of `IntPtr` migrate. 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /proposals/fixed-sized-buffers.md: -------------------------------------------------------------------------------- 1 | # Fixed Sized Buffers 2 | 3 | * [x] Proposed 4 | * [ ] Prototype: Not Started 5 | * [ ] Implementation: Not Started 6 | * [ ] Specification: Not Started 7 | 8 | ## Summary 9 | [summary]: #summary 10 | 11 | Provide a general-purpose and safe mechanism for declaring fixed sized buffers to the C# language. 12 | 13 | ## Motivation 14 | [motivation]: #motivation 15 | 16 | Today, users have the ability to create fixed-sized buffers in an unsafe-context. However, this requires the user to deal with pointers, manually perform bounds checks, and only supports a limited set of types (`bool`, `byte`, `char`, `short`, `int`, `long`, `sbyte`, `ushort`, `uint`, `ulong`, `float`, and `double`). 17 | 18 | The most common complaint is that fixed-size buffers cannot be indexed in safe code. Inability to use more types is the second. 19 | 20 | With a few minor tweaks, we could provide general-purpose fixed-sized buffers which support any type, can be used in a safe context, and have automatic bounds checking performed. 21 | 22 | ## Detailed design 23 | [design]: #detailed-design 24 | 25 | One would declare a safe fixed-sized buffer via the following: 26 | 27 | ```csharp 28 | public fixed DXGI_RGB GammaCurve[1025]; 29 | ``` 30 | 31 | The declaration would get translated into an internal representation by the compiler that is similar to the following 32 | 33 | ```csharp 34 | [FixedBuffer(typeof(DXGI_RGB), 1024)] 35 | public ConsoleApp1.e__FixedBuffer_1024 GammaCurve; 36 | 37 | // Pack = 0 is the default packing and should result in indexable layout. 38 | [CompilerGenerated, UnsafeValueType, StructLayout(LayoutKind.Sequential, Pack = 0)] 39 | struct e__FixedBuffer_1024 40 | { 41 | private T _e0; 42 | private T _e1; 43 | // _e2 ... _e1023 44 | private T _e1024; 45 | 46 | public ref T this[int index] => ref (uint)index <= 1024u ? 47 | ref RefAdd(ref _e0, index): 48 | throw new IndexOutOfRange(); 49 | } 50 | ``` 51 | 52 | Since such fixed-sized buffers no longer require use of `fixed`, it makes sense to allow any element type. 53 | 54 | > NOTE: `fixed` will still be supported, but only if the element type is `blittable` 55 | 56 | ## Drawbacks 57 | [drawbacks]: #drawbacks 58 | 59 | * There could be some challenges with backwards compatibility, but given that the existing fixed-sized buffers only work with a selection of primitive types, it should be possible for the compiler to continue "just-working" if the user treats the fixed-buffer as a pointer. 60 | * Incompatible constructs may need to use slightly different `v2` encoding to hide the fields from old compiler. 61 | * Packing is not well defined in IL spec for generic types. While the approach should work, we will be bordering on undocumented behavior. We should make that documented and make sure other JITs like Mono have the same behavior. 62 | * Specifying a separate type for every length (an possibly another for `readonly` fields, if supported) will have impact on metadata. It will be bound by the number of arrays of different sizes in the given app. 63 | * `ref` math is not formally verifiable (since it is unsafe). We will need to find a way to update verification rules to know that our use is ok. 64 | 65 | ## Alternatives 66 | [alternatives]: #alternatives 67 | 68 | Manually declare your structures and use unsafe code to construct indexers. 69 | 70 | ## Unresolved questions 71 | [unresolved]: #unresolved-questions 72 | 73 | - should we allow `readonly`? (with readonly indexer) 74 | - should we allow array initializers? 75 | - is `fixed` keyword necessary? 76 | - `foreach`? 77 | - only instance fields in structs? 78 | 79 | ## Design meetings 80 | 81 | Link to design notes that affect this proposal, and describe in one sentence for each what changes they led to. -------------------------------------------------------------------------------- /meetings/2016/LDM-2016-08-24.md: -------------------------------------------------------------------------------- 1 | C# Language Design Meeting, Aug 24, 2016 2 | ======================================== 3 | 4 | ## Agenda 5 | 6 | After a meeting-free period of implementation work on C# 7.0, we had a few issues come up for resolution. 7 | 8 | 1. What does it take to be task-like? 9 | 2. Scope of expression variables in initializers 10 | 11 | # Task-like types 12 | 13 | The generalization of async return types requires the return type of an async method to provide an implementation for the "builder object" that the method body's state machine interacts with when it awaits, and when it produces the returned "task-like" value. 14 | 15 | How should it provide this implementation? There are a couple of options: 16 | 17 | 1. A `GetAsyncMethodBuilder` method on the task-like type following a specific compiler-recognized pattern 18 | 2. 1 + a modifier on the type signifying "buy-in" to being an async type 19 | 3. An attribute that encodes the builder type 20 | 21 | We like the attribute approach, because this is really a metadata concern. The attribute sidesteps issues with accessibility (is the type only task-like when the `GetAsyncMethodBuilder` method is accessible?), and doesn't pollute the member set of the type. Also it works on interfaces. For instance, it could conceivably apply to WinRT's `IAsyncAction` etc. 22 | 23 | We only expect very few people to ever build their own task-like types. We may or may not choose to ever actually *ship* the attribute we will use. Implementers can just roll their own internal implementation. The compiler only looks for the name - which will be `System.Runtime.CompilerServices.AsyncBuilderAttribute`. 24 | 25 | There are a couple of options for what the attribute should contain: 26 | 27 | 1. Nothing - it is just a marker, and there is still a static method on the task-like type 28 | 2. The name of a method to call on the task-like type 29 | 3. The builder type itself 30 | 4. A static type for *getting* the builder 31 | 32 | Option 1 and 2 we've already ruled out, because we don't want to depend on accessible members on the task-like type itself. 33 | 34 | Option 3 would require a `Type` argument to the constructor that is either non-generic (for void-yielding or non-generic result-yielding task-like types) or an open generic type with one type parameter (for generic result-yielding task-like types) 35 | Option 4 is essentially a "builder builder", and would let us have a less dirty relationship between type parameters on the builder and task-like: 36 | 37 | ``` c# 38 | static class ValueTaskBuilderBuilder 39 | { 40 | ValueTaskBuilder GetBuilder(ValueTask dummy); 41 | } 42 | ``` 43 | 44 | That is probably a bridge too far, so we will go with Option 3. 45 | 46 | The builder type given in the attribute is required to have a static, accessible, non-generic Create method. 47 | 48 | This would work for `IAsyncAction` etc. from WinRT, assuming that a builder is implemented for them, and an attribute placed on them. 49 | 50 | The proposal to allow the builder to be referenced from the async method is attractive, but something for another day. 51 | 52 | # Scope of expression variables in initializers 53 | 54 | ``` c# 55 | int x = M(out int t), y = t; 56 | ``` 57 | 58 | If this is a local declaration, `t` is in scope after the declaration. If this is a _field_ declaration, though, should it be? It's problematic if it is, and inconsistent if it isn't! Also, if we decide one way or another, we can never compatibly change our mind on it. 59 | 60 | Let's block this off and not allow expression variables to be introduced in field initializers! It's useless today, and we should save the scoping decisions for when future features give us a use case. 61 | 62 | ``` c# 63 | public C() : base(out int x) 64 | { 65 | // use x? 66 | } 67 | ``` 68 | 69 | A variable declared in a `this` or `base` initializer should be in scope in the whole constructor body. 70 | -------------------------------------------------------------------------------- /meetings/2018/LDM-2018-12-12.md: -------------------------------------------------------------------------------- 1 | 2 | # C# LDM notes for Dec 12, 2018 3 | 4 | ## Agenda 5 | 6 | 1. Open issues with async streams 7 | 2. Pattern dispose 8 | 9 | ## Discussion 10 | 11 | ### Cancellation in GetAsyncEnumerator 12 | 13 | We previously decided that GetAsyncEnumerator takes a CancellationToken. 14 | When do we check if it's been cancelled? 15 | 16 | Our design for cancellation in the user code itself is to have the user 17 | write the core logic for their IAsyncEnumerable using an IAsyncEnumerator 18 | iterator method. The user can manually check for cancellation inside the 19 | iterator body. 20 | 21 | ```C# 22 | class C : IAsyncEnumerable 23 | { 24 | IAsyncEnumerator GetAsyncEnumerator(CancellationToken token) 25 | { 26 | ... 27 | token.ThrowIfCancelled(); 28 | ... 29 | } 30 | } 31 | ``` 32 | 33 | *Q: Do we want to generate any checks manually? One option is to check the 34 | cancellation token on every MoveNext call.* 35 | 36 | This comes down to: where is it most useful to request cancellation? 37 | 38 | The most important place to include the cancellation token is in the await 39 | calls themselves. This isn't compiler generated code at all -- if the 40 | expressions being awaited need a CancellationToken the user will need to 41 | pass it themselves. Other places, like every entry to MoveNextAsync, will 42 | be called regularly, but will only be able to cancel synchronous code, 43 | instead of the long-running async operation. 44 | 45 | **Conclusion** 46 | 47 | The compiler will not generate checks for cancellation for now. If we 48 | get user feedback that it's important, we will revisit this decision. 49 | 50 | ### Pattern binding 51 | 52 | Which pattern do we want the compiler to recognize for GetAsyncEnumerator? 53 | 54 | Binding options for some expression target of a `foreach`, `e`: 55 | 56 | 1. `e.GetAsyncEnumerable()` 57 | 2. `e.GetAsyncEnumerable(default(CancellationToken))` 58 | 59 | One of the primary drawbacks for (1) is that all users must make their 60 | CancellationToken parameters optional. This may be a problem for implementors 61 | who consider it very important to have a CancellationToken. 62 | 63 | **Conclusion** 64 | 65 | We will attempt to bind the call 66 | `e.GetAsyncEnumerable(default(CancellationToken))` and if it succeeds and has 67 | a conforming return type, the pattern binds successfully. 68 | 69 | ### Recognizing an iterator 70 | 71 | Consider the following: 72 | 73 | ```C# 74 | class Program 75 | { 76 | static async IAsyncEnumerable M() 77 | { 78 | throw new NotImplementedException(); 79 | } 80 | } 81 | ``` 82 | 83 | As currently designed, this is illegal. `async` methods must either return 84 | a Task-like type or return `IAsyncEnumerable/IAsyncEnumerator` and contain 85 | a yield statement. This is neither. 86 | 87 | The proposal is to produce an error. There are a number of fixes: 88 | 89 | 1. Add a `yield` in the body. 90 | 2. Remove the `async`. 91 | 92 | **Conclusion** 93 | 94 | Add an error, undecided on the error message. 95 | 96 | ### When we start recognizing pattern Dispose, do we call it in foreach? 97 | 98 | Certainly, it seems very important for the feature. Since a ref struct 99 | cannot implement interfaces, pattern Dispose is the only way to implement 100 | disposal. 101 | 102 | There are a number of possible breaking changes: 103 | 104 | 1. A pattern enumerable type did not implement IDisposable, but had a 105 | Dispose method. This method would now be call. 106 | 2. If there are two Dispose extension methods, that will now produce 107 | an ambiguity and thus a compilation error. 108 | 109 | **Conclusion** 110 | 111 | Let's narrow the pattern-based Dispose change down to ref structs only. 112 | This means every other type will be required to implement IDisposable. -------------------------------------------------------------------------------- /meetings/2017/LDM-2017-07-05.md: -------------------------------------------------------------------------------- 1 | # C# Language Design Notes for Jul 5, 2017 2 | 3 | ## Agenda 4 | 5 | Triage of features in the C# 7.2 milestone. They don't all fit: which should be dropped, which should be kept, and which should be pushed out? 6 | 7 | 1. Static delegates *(8.0)* 8 | 2. Native int and IntPtr operators *(7.X)* 9 | 3. Field target *(anytime)* 10 | 4. Utf8 strings *(8.0)* 11 | 5. Slicing *(7.X)* 12 | 6. Blittable *(7.2)* 13 | 7. Ref structs *(7.2)* 14 | 8. Ref readonly *(7.2)* 15 | 9. Conditional ref *(7.2)* 16 | 10. Ref extensions on structs *(7.2)* 17 | 11. Readonly locals and params *(X.X)* 18 | 12. ref structs in tuples *(don't)* 19 | 13. Overload resolution tie breakers with long tuples *(use underlying generics)* 20 | 21 | 22 | # Static delegates 23 | Won't have time, and should probably be in a major release. 24 | 25 | ## Conclusion 26 | 8.0 for now. 27 | 28 | 29 | # Native int and IntPtr operators 30 | This won't make 7.2. 31 | 32 | ## Conclusion 33 | Let's try to keep in 7 wave as 7.X (in case we have more point releases) . Probably only one of these will become a feature, but keeping both for now until we decide. 34 | 35 | 36 | # Field target 37 | Bug fix, do this anytime. 38 | 39 | ## Conclusion 40 | Make sure it is on the Roslyn backlog. 41 | 42 | 43 | # Utf8 strings 44 | There's a bigger story that needs to be worked out here. There needs to be decisions around interpolated strings, formattable strings etc. This feels low on the list, and we don't have enough data to design it yet. 45 | 46 | ## Conclusion 47 | Next major release: 8.0. 48 | 49 | 50 | # Slicing 51 | We would want to add a `Range` type, a syntax `3..5` to create a `Range` and then people can write indexers over ranges to implement slicing. 52 | 53 | We could make it an entirely target-typed thing, where something with a proper constructor could be initialized with a span. Lots to think about. 54 | 55 | Either way, `Span` APIs would eventually want to make use of this, but would not depend on it from the outset. 56 | 57 | ## Conclusion 58 | This won't make 7.2 but keeping it 7.X to keep thinking about it. 59 | 60 | 61 | # Blittable 62 | Small language feature, useful for certain scenarios but not central to 7.2 value proposition. 63 | 64 | ## Conclusion 65 | Let's do it if we get to it. Keep it 7.2, but low pri for that release. 66 | 67 | 68 | # Ref structs 69 | Essential to the scenario and also almost done. 70 | 71 | Still need `stackalloc` rules worked out. We'd prefer to keep that part of the functionality in, but could delay it if necessary. 72 | 73 | ## Conclusion 74 | Stay in 7.2. 75 | 76 | 77 | # Ref readonly 78 | Essential to the scenario and pretty much done. 79 | 80 | ## Conclusion 81 | Keep in 7.2. 82 | 83 | 84 | # Conditional ref 85 | Not essential but already done. 86 | 87 | ## Conclusion 88 | Keep in 7.2. 89 | 90 | 91 | # Ref extensions on structs 92 | Not essential but already done. 93 | 94 | ## Conclusion 95 | Keep in 7.2. 96 | 97 | 98 | # Readonly locals and params 99 | Stands on its own. It has some commonality with 7.2 scenarios, but is not essentially tied. Could be done together with ref readonly locals later. Also it has a lot of design work still to be done. 100 | 101 | ## Conclusion 102 | Push to X.X. 103 | 104 | 105 | # ref structs in tuples 106 | Tuples can't contain ref structs because those can't be type arguments. 107 | 108 | ## Conclusion 109 | That's a big pandora's box to open, and we won't. 110 | 111 | 112 | # Overload resolution tie breakers with long tuples 113 | There's an esoteric difference between whether "specificity" tie breaker should special case long tuple as a flat list, or should work according to the underlying generics, which are nested. 114 | 115 | ## Conclusion 116 | We stick with how it works now, which is the underlying generics. 117 | 118 | --------------------------------------------------------------------------------