Merge remote-tracking branch 'refs/remotes/origin/master' into design-notes

This commit is contained in:
Mads Torgersen 2018-05-01 13:16:51 -07:00
commit 7f4c64b9d4
70 changed files with 1611 additions and 681 deletions

View file

@ -22,7 +22,6 @@ Features Added in C# Language Versions
- Nullable types
- Getter/setter separate accessibility
- Method group conversions (delegates)
- Co- and Contra-variance for delegates and interfaces
- Static classes
- Delegate inference
@ -40,7 +39,7 @@ Features Added in C# Language Versions
# [C# 4](https://msdn.microsoft.com/en-us/magazine/ff796223.aspx) (VS 2010)
- Dynamic binding
- Named and optional arguments
- Generic co- and contravariance
- Co- and Contra-variance for generic delegates and interfaces
- Embedded interop types ("NoPIA")
# [C# 5](https://blogs.msdn.microsoft.com/mvpawardprogram/2012/03/26/an-introduction-to-new-features-in-c-5-0/) (VS 2012)
@ -75,9 +74,17 @@ Features Added in C# Language Versions
- More expression-bodied members
- [Throw expressions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.0/throw-expression.md)
# [C# 7.1](https://github.com/dotnet/roslyn/blob/master/docs/Language%20Feature%20Status.md) (Visual Studio 2017 version 15.3)
# [C# 7.1](https://blogs.msdn.microsoft.com/dotnet/2017/10/31/welcome-to-c-7-1/) (Visual Studio 2017 version 15.3)
- [Async main](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/async-main.md)
- [Default expressions](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/target-typed-default.md)
- [Reference assemblies](https://github.com/dotnet/roslyn/blob/master/docs/features/refout.md)
- [Inferred tuple element names](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/infer-tuple-names.md)
- [Pattern-matching with generics](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/generics-pattern-match.md)
# [C# 7.2](https://blogs.msdn.microsoft.com/dotnet/2017/11/15/welcome-to-c-7-2-and-span/) (Visual Studio 2017 version 15.5)
- [Span and ref-like types](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/span-safety.md)
- [In parameters and readonly references](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/readonly-ref.md)
- [Ref conditional](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/conditional-ref.md)
- [Non-trailing named arguments](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/non-trailing-named-arguments.md)
- [Private protected accessibility](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/private-protected.md)
- [Digit separator after base specifier](https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/leading-separator.md)

View file

@ -1,5 +1,7 @@
# C# Language Design
[![Join the chat at https://gitter.im/dotnet/csharplang](https://badges.gitter.im/dotnet/csharplang.svg)](https://gitter.im/dotnet/csharplang?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Welcome to the official repo for C# language design. This is where new C# language features are developed, adopted and specified.
C# is designed by the C# Language Design Team (LDT) in close coordination with the [Roslyn](https://github.com/dotnet/roslyn) project, which implements the language.

View file

@ -0,0 +1,135 @@
# C# Design Notes for Oct 21, 2013
## Agenda
Primary constructors is the first C# 6.0 feature to be speced and implemented. Aleksey, who is doing the implementing joined us to help settle a number of details that came up during this process. We also did a rethink around Lightweight Dynamic.
1. Primary Constructors <fleshed out a few more details>
2. Lightweight Dynamic <we examined a much simpler approach>
## Primary Constructors
Primary constructors were first discussed in the design meeting on Apr 15, where we decided to include them, but have been around as a suggestion for much longer. It has great synergy with features such as initializers on auto-properties (May 20), because it puts constructor parameters in scope for initializers.
The evolving “speclet” for primary constructors can be found in the “Csharp 6.0 Speclets” working document in the design notes archive (see link above). It reflects a number of implementation choices, and also (at the time of the meeting) left some issues open, which we settled as best we could. Some decisions may change as we get experience with the feature indeed the early focus on implementing this feature is so that we can incorporate feedback from usage.
### Partial types
Primary constructors involve a parameter list on “the” class or struct header, as well as an optional argument list on “the” base class specification. But what if the type declaration is split over multiple partial types?
There is different precedence with partial classes. Sometimes elements can occur in only one part (members), sometimes they must occur and be the “same” in all parts (type parameters), sometimes they are optional but must agree where present (accessibility and constraints) and sometimes they are cumulative over parts (attributes).
There seems to be little value in allowing class parameters for primary constructors to occur in multiple parts indeed it would require us to come up with a notion of sameness that only adds complication to the language. So class parameters are allowed only on one of the parts. Arguments to the base class are allowed only in the part where the class parameters occur. The class parameters are in scope in all the parts. This is also the approach that most resembles what is the case today: constructor parameters occur only in one place, and the privates used to hold their values are in scope in all the parts.
### User-provided constructor body
As currently specified, primary constructors give rise to a constructor that is entirely compiler-generated. There is no syntax for the developer to specify statements to be executed as part of the primary constructor invocation. This is less of an issue than it might seem, since more than 90% of what people do in constructor bodies is probably what primary constructors now do for them copying parameter values into private fields. Even initialization of other fields can now usually take place in initializers, because the constructor parameters are in scope for those.
Even so, for the remaining scenarios the user would fall off a cliff: they would have to renounce primary constructors and turn them back into explicit constructors. This is a shame, and it is definitely worth considering what syntax we would use to fix this.
A radical idea is to just allow statements directly in the class body. This would be weird for several reasons though. Aside from syntactic issues, theres the problem of evaluation order relative to fields with initializers: initializers run before the base call, whereas statements in a constructor body usually run after, so interspersing them would have very surprising results.
In reality wed need to place the statements in a block, and the only question is how we “dress up” the block if at all. Some ideas:
``` c#
class C(int x): B(x)
{
// Repeat the full signature redundant and error prone
C(int x) { Register(this); }
// Class name and empty parens clashes with noarg constructor syntax,
// but similar to static constructors
C() { Register(this); }
// Just the class name a little unfamiliar
C { Register(this); }
// Prefix with this keyword again a bit unfamiliar
this { Register(this); }
// Just the block succinct but mysterious and has the eval order issue
{ Register(this); }
}
```
Many of these options are reasonable. Well hold off though and see how the feature fares without user specified statements for now.
### Struct issues
Structs always have a default constructor. Should we allow explicit constructors in a struct with a primary constructor to delegate to the default constructor instead of the primary constructor? I.e. is the following ok?
``` c#
struct S(int x)
{
public S(bool b) : this() { … }
}
```
It seems that the restriction on explicit constructors to always chain to the primary constructor cannot be upheld just by requiring a this(…) initializer on them: we must also require that it isnt referencing the default (no-arg) constructor.
Also, members in structs are allowed to assign to this. Should we allow that also in constructor bodies when there are primary constructors? We probably cannot reasonably prevent it, and people who do this are already in the advanced category. But we have to spec that it causes the primary constructor parameters to be reset to their default value.
### Attribute targets
Currently, we allow “method” targets on constructors. We should allow an attribute on a class or a struct with a primary constructor to specify a “method” target in order to be applied to the underlying generated constructor.
We should also allow attributes on the individual parameters of the primary constructor to specify a “field” target in order to be applied to the underlying field. In practice there may be optimizations so that the field is not generated when not needed, but the field target should force the field to be generated.
### Accessibility on primary constructor parameters
There is an idea known from other languages (e.g. Scala, TypeScript) of allowing accessibility modifiers on class parameters. This would mean that a property is generated on the class with the name of the parameter, and the accessibility specified.
This would make some scenarios even more succinct, but it is not a straightforward feature. For instance, parameter names are typically lower case and property names typically upper case. We think this is a feature that can be added later if we feel there is significant further gain. Right now we are willing to bet on the combination of primary constructors and more terse property specification (e.g. with expression bodied properties and initializers for auto-properties) as a sufficiently great advance in the specification of data-like classes and structs.
### Conclusion
We continue to bake the details of primary constructors.
## Lightweight Dynamic
We took a fresh look at Lightweight Dynamic, based on feedback from Dino, who used to work on the “old” dynamic infrastructure.
The purpose of Lightweight Dynamic is to allow the behaviors of operations on an object to be specified programmatically, as part of the implementation of the objects class. Dino pointed out that most behaviors can already be specified, using operator overloading, user defined conversions and indexers. Really, the “only” things missing are invocation and dotting. So instead of coming up with yet another new infrastructure for programmatic definition of object behaviors, we should just augment the one weve had all along with facilities for invocation and member access.
Doing more with less is a pretty attractive idea! So we experimented with what that would look like.
### Invokers
It seems reasonable to allow specification of what it means to invoke an object. The scenario seems similar to indexers, which are a special kind of instance members that can be abstract and virtual, take arguments, and be overloaded on signature.
The main difference would be that parameters are specified in round parentheses, and that the body is simply a method body, rather than a pair of accessors:
``` c#
public int this(int x) { return x*x; } // allow instances to be invoked with ints
```
One could imagine using this feature to build alternatives to delegate types, but more interestingly, this could provide semi-typed invocation capabilities for e.g. service calls:
``` c#
public object this(params object[] args) { … } // generalized dynamic invoker
```
While it is straightforward to imagine this feature, it is also of relatively limited value: You can always provide an explicit Invoke method that the user can call at little extra cost.
``` c#
myOperation.Invoke(5, "Hello"); // instead of
myOperation(5, "Hello");
```
Even so, it is interesting to consider.
### Member access
For programmatic member access it seems superficially attractive or at least powerful to allow overriding of the dot operator. Maybe in the form of some kind of operator overload?
``` c#
public static JsonObject operator .(JsonObject receiver, string name) { … }
```
The problem of course is that dot is such a fundamental operator. If you overload it to hide the native meaning, you can hardly do anything with the underlying object. How would you even implement the dot operator without using the built in dot for accessing state etc. on the object?
You could of course have rules like we do for events, where the meaning is different on the inside and the outside. But that is not exactly elegant. The fact of the matter is that overriding dot on a statically typed class is simply too disruptive.
Here is where Visual Basic may provide some interesting inspiration. The typical way youd implement the dot operator would be some sort of dictionary lookup, and in fact youd probably be fine if dotting did the same as indexing with a string, only with slightly more elegant syntax. Well, VB has an operator for that: The lookup operator, `!`.
We could essentially resign ourselves to not using dot for “lightweight dynamic” member access, instead using `!` as a just-as-short substitute. We would then simply define these two expressions to mean the same:
``` c#
obj!x
obj["x"]
```
If you want to implement dynamic behavior for member access, you just provide an indexer over strings. Your callers will have to use a `!` instead of a `.`, but that is probably a good thing: that way they can still access your “real” members using a normal dot. This was a real issue with the Lightweight dynamic model weve explored before, one that we had yet to find a good solution to.
### Conclusion
We still need to dig deeper here, but we think that adding the `!` lookup operator as a short hand for indexing with a string may quite possibly be the only work we have to do in the language for lightweight dynamic! That is quite possibly the biggest reduction of work associated with a feature weve ever accomplished in a single sitting!

View file

@ -0,0 +1,101 @@
# C# Design Notes for Nov 4, 2013
## Agenda
Next up for implementation are the features for auto-properties and function bodies, both of which go especially well with primary constructors. For the purpose of specing those, we nailed down a few remaining details. We also took another round discussing member access in the lightweight dynamic scenario.
1. Initialized and getter-only auto-properties <details decided>
2. Expression-bodied function members <details decided>
3. Lightweight dynamic <member access model and syntax discussed>
## Initialized and getter-only auto-properties
We are allowing initializers for auto-properties, and we are allowing getter-only auto-properties, where the underlying field is untouched after the initializer has run. This was last discussed on May 20, and allows declarations like this:
``` c#
public bool IsActive { get; } = true;
```
In order to spec these feature additions there are a couple of questions to iron out.
## Getter-only auto-properties without initializers
Initializers will be optional on get-set auto-properties. Should we allow them to be left out on getter-only properties?
``` c#
public bool IsActive { get; } // No way to get a non-default value in there
```
Theres a symmetry and simplicity argument that we should allow this. However, anyone writing it is probably making a mistake, and even if they really meant it, they (and whoever inherits their code) are probably better off explicitly initializing to the default value.
### Conclusion
Well disallow this.
### Is the backing field of getter-only auto-properties read-only?
Theres an argument for fewer differences with get-set auto-properties. However, the field is really never going to change, and any tool that works on the underlying field (e.g. at the IL level) would benefit from seeing that it is indeed readonly.
Readonly does not prevent setting through reflection, so deserialization wouldnt be affected.
### Conclusion
Lets make them readonly. Theres a discrepancy with VBs decision here, which should be rationalized.
## Expression-bodied function members
This feature would allow the body of methods and of getter-only properties to be expressed very concisely with a single expression. This feature was last discussed on April 15, and would allow code like this:
``` c#
public class Point(int x, int y) {
public int X => x;
public int Y => y;
public double Dist => Math.Sqrt(x * x + y * y);
public Point Move(int dx, int dy) => new Point(x + dx, y + dy);
}
```
Again, there are a few details to iron out.
### Are we happy with the syntax for expression-bodied properties?
The syntax is so terse that it may not be obvious to the reader that it is a property at all. It is just one character off from a field with an initializer, and lacks the telltale get and/or set keywords.
One could imagine a slightly more verbose syntax:
``` c#
public int X { get => x; }
```
This doesnt read quite as well, and gives less of an advantage in terms of terseness.
### Conclusion
Well stick with the terse syntax, but be open to feedback if people get confused.
## Which members can have expression bodies?
While methods and properties are clearly the most common uses, we can imagine allowing expression bodies on other kinds of function members. For each kind, here are our thoughts.
### Operators: Yes
Operators are like static methods. Since their implementations are frequently short and always have a result, it makes perfect sense to allow them to have expression bodies:
``` c#
public static Complex operator +(Complex a, Complex b) => a.Add(b);
```
### User defined conversions: Yes
Conversions are just a form of operators:
``` c#
public static implicit operator string(Name n) => n.First + " " + n.Last;
```
### Indexers: Yes
Indexers are like properties, except with parameters:
``` c#
public Customer this[Id id] => store.LookupCustomer(id);
```
### Constructors: No
Constructors have syntactic elements in the header in the form of this(…) or base(…) initializers which would look strange just before a fat arrow. More importantly, constructors are almost always side-effecting statements, and dont return a value.
### Events: No
Events have add and remove accessors. Both must be specified, and both would be side-effecting, not value returning. Not a good fit.
### Finalizers: No
Finalizers are side effecting, not value returning.
### Conclusion
To summarize, expression bodies are allowed on methods and user defined operators (including conversions), where they express the value returned from the function, and on properties and indexers where they express the value returned from the getter, and imply the absence of a setter.
### void-returning methods
Methods returning void, and async methods returning task, do not have a value-producing return statement. In that respect they are like some of the member kinds we rejected above. Should we allow them all the same?
### Conclusion
We will allow these methods to have expression bodies. The rule is similar to expression-bodied lambdas: the body has to be a statement expression; i.e. one that is “certified” to be executed for the side effect.
## Lightweight dynamic member access
At the design meeting on Oct 21, we discussed options for implementing “user-defined” member access. There are two main directions:
- Allow users to override the meaning of dot
- Introduce a “dot-like” syntax for invoking string indexers
The former really has too many problems, the main one being what to do with “real” dot. Either we make “.” retain is current meaning and only delegate to the user-supplied meaning when that fails. But that means the new dot does not get a full domain of names. Or we introduce an escape hatch syntax for “real dot”. But then people would start using that defensively everywhere, especially in generated code. It also violates substitutability where o.x suddenly means something else on a subtype than a supertype.
We strongly prefer the latter option, providing a “dot-like” syntax that translates into simply invoking a string indexer with the provided identifier as a string. This has the added benefit of working with existing types that have string indexers. The big question of course is what dot-like syntax. Here are some candidates we looked at in order to home in on a design philosophy:
``` c#
x!Foo // Has a dot, but not as a separate character
x."Foo" // That looks terrible, too much like any value could be supplied
x.[Foo] // Doesnt feel like Foo is a member name
x.#Foo // Not bad
x.+Foo // Hmmmm
```
Some principles emerge from this exercise:
- Using "…" or […] kills the illusion of it being a member name
- Having characters after the identifier breaks the flow
- Using a real dot feels right, and would help with the tooling experience
- What comes after that real dot really matters!
### Conclusion
We like the “dot-like” syntax approach to LW dynamic member access, and we think it should be of the form x.<glyph>Foo for some value of <glyph>.

View file

@ -6,15 +6,28 @@ Overview of meetings and agendas for 2013
[C# Language Design Notes for Oct 7, 2013](LDM-2013-10-07.md)
1. Invariant meaning of names <_scrap the rule_>
2. Type testing expression <_cant decide on good syntax_>
3. Local functions <_not enough scenarios_>
4. nameof operator <_yes_>
1. Invariant meaning of names <*scrap the rule*>
2. Type testing expression <*cant decide on good syntax*>
3. Local functions <*not enough scenarios*>
4. nameof operator <*yes*>
[C# Language Design Notes for Oct 21, 2013](LDM-2013-10-21.md)
1. Primary Constructors <*fleshed out a few more details*>
2. Lightweight Dynamic <*we examined a much simpler approach*>
## Nov 4, 2013
[C# Language Design Notes for Nov 4, 2013](LDM-2013-11-04.md)
1. Initialized and getter-only auto-properties <*details decided*>
2. Expression-bodied function members <*details decided*>
3. Lightweight dynamic <*member access model and syntax discussed*>
## Dec 16, 2013
[C# Language Design Notes for Dec 16, 2013](LDM-2013-12-16.md)
1. Declaration expressions <_reaffirmed scope rules, clarified variable introduction_>
2. Semicolon operator <_reaffirmed enclosing parentheses_>
3. Lightweight dynamic member access <_decided on a syntax_>
1. Declaration expressions <*reaffirmed scope rules, clarified variable introduction*>
2. Semicolon operator <*reaffirmed enclosing parentheses*>
3. Lightweight dynamic member access <*decided on a syntax*>

View file

@ -12,7 +12,7 @@ There are a couple of places where declaration expressions are grammatically amb
There are two kinds of full ambiguities (i.e. ones that dont resolve with more lookahead):
``` c#
a * b // multiplication expression or unitialized declaration of pointer?
a * b // multiplication expression or uninitialized declaration of pointer?
a < b > c // nested comparison or uninitialized declaration of generic type?
```
The latter one exists also in a method argument version that seems more realistic:

View file

@ -40,7 +40,7 @@ The best way for us to deal with this is to simply disallow automatic capture. T
}
```
Now this raises a new problem. What if you want to capture a constructor parameter in a private field and have no intention of exposing it publically. You can do that explicitly:
Now this raises a new problem. What if you want to capture a constructor parameter in a private field and have no intention of exposing it publicly. You can do that explicitly:
``` c#
public class Person(string first, string last)

View file

@ -28,7 +28,7 @@ The notion of referenced set has little importance for the language-level semant
Reference to some entities, e.g. obsolete members, `Finalize` or `op_` methods, is normally an error. However, it is not an error in `nameof(…)` unless _all_ members of the referenced set would give an error. If all non-error references give warnings, then a warning is given.
### The resulting string
C# doesnt actually have a notion of canonical name. Instead, equality between names is currently defined directly _beween_ names that may contain special symbols.
C# doesnt actually have a notion of canonical name. Instead, equality between names is currently defined directly _between_ names that may contain special symbols.
For `nameof(… i)` we want the resulting string to be the identifier `I` given, except that formatting characters are omitted, and Unicode escapes are resolved. Also, any leading `@` is removed.

View file

@ -73,11 +73,11 @@ Class C
Public Sub New()
f(x)
Dim lamda = Sub()
f(x) ' error BC36602: 'ReadOnly' variable
' cannot be the target of an assignment in a lambda expression
' inside a constructor.
End Sub
Dim lambda = Sub()
f(x) ' error BC36602: 'ReadOnly' variable
' cannot be the target of an assignment in a lambda expression
' inside a constructor.
End Sub
End Sub
Shared Sub f(ByRef x As Integer)
x = 23
@ -102,7 +102,7 @@ __Workarounds__
4. Write documentation for the interface, on MSDN or in XML Doc-Comments, that say "Internal class only; do not implement it". We see this for instance on ICorThreadpool.
5. Declare a method on the interface which has an internal type in its signature. The CLR allows this but the language doesn't so it would have to be authored in IL. Every type which implements the interface would have to provide an implementation of that method.
6. Write run-time checks at the public entry points of key Roslyn methods that take in an ISymbol, and throw if the object given was implemented in the wrong assembly.
7. Write a Roslyn analyzer which is deployed by the same Nuget package that contains the definition of ISymbol, and have this analyzer warn if you're trying to implement the interface. This analyzer could be part of Roslyn, or it could be an independent third-party analyzer used by many libraries.
7. Write a Roslyn analyzer which is deployed by the same NuGet package that contains the definition of ISymbol, and have this analyzer warn if you're trying to implement the interface. This analyzer could be part of Roslyn, or it could be an independent third-party analyzer used by many libraries.
__Proposal:__ Have the compiler recognize a new attribute. Given the following code

View file

@ -195,7 +195,7 @@ Metaprogramming
Metaprogramming has been around as a theme on the radar for a long time, and arguably Roslyn is a big metaprogramming project aimed at writing programs about programs. However, at the language level we continue not to have a particularly good handle on metaprogramming.
Extention methods and partial classes both feel like features that could grow into allowing *generated* parts of source code to merge smoothly with *hand-written* parts. But if generated parts are themselves the result of language syntax - e.g. attributes in source code, then things quickly get messy from a tooling perspective. A keystroke in file A may cause different code to be generated into file B by some custom program, which in turn may change the meaning of A. Not a feedback loop we're eager to have to handle in real time at 20 ms keystroke speed!
Extension methods and partial classes both feel like features that could grow into allowing *generated* parts of source code to merge smoothly with *hand-written* parts. But if generated parts are themselves the result of language syntax - e.g. attributes in source code, then things quickly get messy from a tooling perspective. A keystroke in file A may cause different code to be generated into file B by some custom program, which in turn may change the meaning of A. Not a feedback loop we're eager to have to handle in real time at 20 ms keystroke speed!
Oftentimes the eagerness to generate source comes from it being too hard to express your concept beautifully as a library or an abstraction. Increasing the power of abstraction mechanisms in the language itself, or just the syntax for applying them, might remove a lot of the motivation for generated boilerplate code.

View file

@ -71,7 +71,7 @@ As a case study, has Roslyn suffered from the lack of this feature? There have b
Probably not. Roslyn is *not* immutable. It's presenting an immutable *view*, but is mutable inside. Would that be the common case, though?
Some of the "cheating" in Roslyn (caching, free lists, etc) is for performance, some is for representing cycles. Insofar as the immutable types feature is *also* for performance, it seems that thhere's a tension between using it or not.
Some of the "cheating" in Roslyn (caching, free lists, etc) is for performance, some is for representing cycles. Insofar as the immutable types feature is *also* for performance, it seems that there's a tension between using it or not.
In summary, we are unsure of the value. Let's talk more.
@ -136,7 +136,7 @@ if (e != null) {
var a = e.Expr as AssignmentExpressionSyntax;
if (a != null) {
var l = a.Left as IdentifierName;
var r = a.RIght as IdentifierName;
var r = a.Right as IdentifierName;
if (l != null && r != null & l.Name.name == r.Name.name) ...
```

View file

@ -20,7 +20,7 @@ In this meeting we looked over the top [C# language feature requests on UserVoic
8. XML comments (*Not a language request*)
9. Unmanaged constraint (*requires CLR support*)
10. Compilable strings (*this is what nameof is for*)
11. Mulitple returns (*working on it, via tuples*)
11. Multiple returns (*working on it, via tuples*)
12. ISupportInitialize (*too specific; hooks on object initializers?*)
13. ToNullable (*potentially part of nullability support*)
14. Statement lambdas in expression trees (*fair request, big feature!*)
@ -125,7 +125,7 @@ This would be great in order to enable pointers over type parameters. However, i
This is mostly addressed by `nameof` in C# 6; it's unlikely there is basis for more language level functionality here.
11\. Mulitple returns
11\. Multiple returns
=====================
[http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2083753-return-multiple-values-from-functions-effortlessly](http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2083753-return-multiple-values-from-functions-effortlessly)

View file

@ -18,7 +18,7 @@ In a typical scenario a lambda expression tree is produced in one place, ideally
Along the way, several transformations are often made on the trees, for efficiency reasons etc. For instance, rather than invoke a lambda its body can often be inlined in the enclosing tree that the lambda gets stitched into.
The serialization format is able to carry very specific type information along with the code, but can also represent the code loosely. A looser coupling makes for code that is more resilient to "schema" differences between the nodes, and also allows for use of types and functions that aren't present where the lambda is concocted. However, it also makes it harder to stich things back up right on the other side.
The serialization format is able to carry very specific type information along with the code, but can also represent the code loosely. A looser coupling makes for code that is more resilient to "schema" differences between the nodes, and also allows for use of types and functions that aren't present where the lambda is concocted. However, it also makes it harder to stitch things back up right on the other side.

View file

@ -12,7 +12,7 @@ A summary of the design we (roughly) landed on in #5031 was put out on GitHub as
# Array creation with non-nullable types
For array creation there is the question whether to allow (big hole) or disallow (big nuisance?) on non-nullable referance types. We'll leave it at allow for now, but may reconsider.
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.
# Null checking operator

View file

@ -92,7 +92,7 @@ On closer scrutiny, though, this scenario does raise harder issues. If you use a
These are questions we don't want to answer right now for a somewhat hypothetical scenario.
**Conclusion**: for the propotype, ref locals are non-reassignable, and get their "safe-to-return" status from their mandatory ref initializer.
**Conclusion**: for the prototype, ref locals are non-reassignable, and get their "safe-to-return" status from their mandatory ref initializer.
If there are scenarios not covered by this, we'll discover as folks start using the prototype and tell us.

View file

@ -35,7 +35,7 @@ We like this.
What do branches mean
=====================
As prototypes start to come online, we need to decide how we use our pbranches. The Future branch is for when things are at prototype level for the whole experience - including IDE. Features will be guarded by a feature flag until it's the confident plan of record to ship the feature in its current form.
As prototypes start to come online, we need to decide how we use our branches. The Future branch is for when things are at prototype level for the whole experience - including IDE. Features will be guarded by a feature flag until it's the confident plan of record to ship the feature in its current form.
We are working on enabling VSIXes to change the language model in the IDE without the need for customers to install over VS. Hopefully this capability will be available in an update to VS in a not too distant future. Once that is in place, we can ship prototypes that run without risk to customers' VS installation.

View file

@ -116,7 +116,7 @@ Along with this, we'd need to consider whether to extend the query syntax in the
## Language support
In the language we would add support for foreach'ing over async sequences to consume them, and for async iterators to produce them. Additionally (we don't discuss that further here) we may want to introduce a notion of `IAsyncDisposable`, for weach we could add an async version of the `using` statement.
In the language we would add support for foreach'ing over async sequences to consume them, and for async iterators to produce them. Additionally (we don't discuss that further here) we may want to introduce a notion of `IAsyncDisposable`, for which we could add an async version of the `using` statement.
One concern about async versions of language features such as foreach (and using) is that they would generate `await` expressions that aren't there in source. Philosophically that may or may not be a problem: do you want to be able to see where all the awaiting happens in your async method? If that's important, we can maybe add the `await` or `async` keyword to these features somewhere:

View file

@ -44,7 +44,7 @@ resort.
- One issue around user-defined conversions to switchable types is
resolved (https://github.com/dotnet/roslyn/issues/4944). In the draft spec,
a conversion will be applied on the `case`s, not on the control-expression unilaterally.
Instead of converting only to `swithable` types, each
Instead of converting only to `switchable` types, each
`case` arm will consider any conversions that allow the `case` to be applied.
Any given conversion would be applied at most once.

View file

@ -81,7 +81,7 @@ In this meeting we looked over the top [C# language feature requests on UserVoic
8. XML comments (*Not a language request*)
9. Unmanaged constraint (*requires CLR support*)
10. Compilable strings (*this is what nameof is for*)
11. Mulitple returns (*working on it, via tuples*)
11. Multiple returns (*working on it, via tuples*)
12. ISupportInitialize (*too specific; hooks on object initializers?*)
13. ToNullable (*potentially part of nullability support*)
14. Statement lambdas in expression trees (*fair request, big feature!*)

View file

@ -127,7 +127,7 @@ p.With("Minney", p.LastName)
We can decide whether to make with-expressions _require_ a `With` method, or fall back to constructor calls in its absence.
If we _require_ a `With` method, that makes for less interoperability with existing types. However, it gives us new opportunities for how to provide the position/name mapping metadata thorugh the declaration of that `With` method: For instance, we could introduce a new kind of default parameter that explicitly wires the parameter to a property:
If we _require_ a `With` method, that makes for less interoperability with existing types. However, it gives us new opportunities for how to provide the position/name mapping metadata through the declaration of that `With` method: For instance, we could introduce a new kind of default parameter that explicitly wires the parameter to a property:
``` c#
public abstract Person With(string firstName = this.FirstName, string lastName = this.LastName);

View file

@ -84,7 +84,7 @@ We think no. the existence of a `Deconstruct` method should not imply conversion
We could consider letting user defined implicit conversion imply `Deconstruct`. It leads to some convenience, but makes for a less clean correspondence with consumption code.
Let's keep it separate. If you want a type to be both deconstructible and convertible to tuple, you need to specify both.
Let's keep it separate. If you want a type to be both deconstructable and convertible to tuple, you need to specify both.
# Anonymous types
Should they implement `Deconstruct` and `ITuple`, and be convertible to tuples?

View file

@ -58,7 +58,7 @@ Should we make existing types "span-aware"? Arrays? Strings?
We need to figure out what the right trade-off is.
Generally we would keep Span<T> at the 1% of users. It's analogous to array in the sense that it's arerely used directly in higher-level code.
Generally we would keep Span<T> at the 1% of users. It's analogous to array in the sense that it's rarely used directly in higher-level code.
For the rest there's wrapper types that are not limited to the stack, and can go into Span-land on demand.
@ -155,7 +155,7 @@ Limitations around ref locals today
There are many limitations to keep the rules simple, including the fact that ref locals are readonly at the ref level.
What should the restrictions be around ref-like types? One difference is thate.g. Span has a default value; 0-length span.
What should the restrictions be around ref-like types? One difference is that e.g. Span has a default value; 0-length span.
Next steps

View file

@ -82,7 +82,7 @@ When there are expression variables in the case header, in the case body forbid:
Drop the subbullets for now, and maybe we can relax later
Out of all these approaches, we still think expanding the lifeltime (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.
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.

View file

@ -13,7 +13,7 @@ Can't have an enumerator be shared between multiple threads.
You can imagine channel-like data sources that provide enumerators but each enumerator gets a distinct set of values.
The interface is just giving you an access pattern, not a contract. They could be "hot" or "cold" / repeatedable or not.
The interface is just giving you an access pattern, not a contract. They could be "hot" or "cold" / repeatable or not.
Should we be close to Channel or is that something else?

View file

@ -77,8 +77,8 @@ There's also a more implementation-oriented issue:
When generating code for an await, we need to "spill" the stack, including any variable refs sitting on it, to the heap. Since refs cannot be in the heap, we do tricks: since we know the variable may need spilling, we don't actually resolve it in place. Instead we keep around the constituent parts (e.g. an array and an index, an object and a member, etc) and only look it up *after* the code has resumed after the await.
But here we cannot easily store the constituent parts, because we won't know which branch of the conditional will end up getting executed, and each may lead to different forms of constituent parts! While there may be extremely sneaky and possibly expensive ways around this, we could also just forbid the await. However, the error message would be very strange indeed if it came on an ordinary shape of conditional, whereas it is easier to say refering to a special syntactic form of the conditional.
But here we cannot easily store the constituent parts, because we won't know which branch of the conditional will end up getting executed, and each may lead to different forms of constituent parts! While there may be extremely sneaky and possibly expensive ways around this, we could also just forbid the await. However, the error message would be very strange indeed if it came on an ordinary shape of conditional, whereas it is easier to say referring to a special syntactic form of the conditional.
## Conclusion
On the whole, these challenges lead us to prefer the original proposal with the explicit occurences of `ref` in the syntax.
On the whole, these challenges lead us to prefer the original proposal with the explicit occurrences of `ref` in the syntax.

View file

@ -56,7 +56,7 @@ Attributes on lambdas seem like a more general feature. Let's feed this in as a
Best approach is the small leak pattern which creates a wrapper delegate with a weak reference to the original receiver. That doesn't solve the whole problem, though - how about unregistering, etc?
This isn't necessarily a language feature. This should be a library that you pass the method to, and it wraps it. It would be helped by the langauge allowing for a delegate constraint.
This isn't necessarily a language feature. This should be a library that you pass the method to, and it wraps it. It would be helped by the language allowing for a delegate constraint.
Conclusion: Not as a language feature right now; figure it out as a library feature, and *maybe*, someday we would add syntax around it.

View file

@ -21,7 +21,7 @@ Discussion of default interface member implementations, based on [this guided to
# Concerns raised in various fora
- Traits: Not what we're out to address! It may have similar capabilites, but the design point is different, based on different motivations.
- Traits: Not what we're out to address! It may have similar capabilities, but the design point is different, based on different motivations.
- We are also *not* out to add multiple inheritance to the language - we already have it with interfaces, and we're just making sure that continues to work.
- It's a bit funny that members of interfaces are inherited into interfaces but not into classes.

View file

@ -138,7 +138,7 @@ This is expected to come with heavy tooling costs, because we need to wire throu
## Library self-opt-in
This approach lets the library express, through attributes, whether to consider its unnanotated types non-nullable. The default would be no, so that an existing nullability-unaware library would continue working as it does today, i.e. lead to no warnings.
This approach lets the library express, through attributes, whether to consider its unannotated types non-nullable. The default would be no, so that an existing nullability-unaware library would continue working as it does today, i.e. lead to no warnings.
We sometimes call this option "URTANN" because the meaning of the attribute is "**U**nannotated **R**eference **T**ypes **A**re **N**on-**N**ullable".

View file

@ -33,7 +33,7 @@ We could generalize `!` to silencing all nullability warnings even in subexpress
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.
We can make `!!` an error. If you really want two conecutive bangs (we don't believe there's *any* scenario, other than swearing) you can parenthesize: `(e!)!`.
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!)!`.
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.

View file

@ -16,7 +16,7 @@ The fact that span is array like is not very interesting for the safety rules. T
Once you can embed refs in struct, the question: How can you assign them?
With assigment, you can now assign to ref parameters of ref-like structs, so every assignment is a potential return.
With assignment, you can now assign to ref parameters of ref-like structs, so every assignment is a potential return.
"Let's not refer to local data" does not work here:

View file

@ -21,7 +21,7 @@ Put it in X.X with a note to consider again when we have match expressions
There's a "there" there.
We think this should be addressed, and will keep the chanpioning issue to represent it.
We think this should be addressed, and will keep the championing issue to represent it.
However, it should be different:

View file

@ -127,7 +127,7 @@ This is probably right. We'll think about this more, but let's go with covarianc
# Null warnings
Feedback: Fine to warn in most places where a null value is given a nonnullable type, but we should beware of a "sea of warnings" effect. Specifically, we shouldn't warn on array creation, as in `new string[10]`, even though it creates a hole array of undesired nulls, because it is so common in current code, and couldn't have been done "safely" before.
Feedback: Fine to warn in most places where a null value is given a nonnullable type, but we should beware of a "sea of warnings" effect. Specifically, we shouldn't warn on array creation, as in `new string[10]`, even though it creates a whole array of undesired nulls, because it is so common in current code, and couldn't have been done "safely" before.
## Conclusion

View file

@ -74,7 +74,7 @@ For now, let's put it in 8.0. We don't believe it's going to make 7.3, but that
# 179
For people who care about perf, they already need 3 or 4 overloads, and this would be yet another overload peopple will yell at them to add.
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.
For no parameters today, you no longer need an overload to avoid allocation, because we now use `Array.Empty`.

View file

@ -103,7 +103,7 @@ It's a zero-conceptual-overhead feature.
Original designers left in space between meanings for a purpose. But that's not so compelling to us anymore.
Because it touches overload resolution, it might be better alligned with a .0 release. But we're not compelled by that.
Because it touches overload resolution, it might be better aligned with a .0 release. But we're not compelled by that.
Let's keep it, but again, it's cuttable.

View file

@ -47,7 +47,7 @@ Should it be a verb/command? Not many expression keywords are that (`select` is
};
```
The last one is subject to ambiguity-like situations betyween expression and statement `switch`.
The last one is subject to ambiguity-like situations between expression and statement `switch`.
We should also consider nesting of match expressions.

View file

@ -290,309 +290,6 @@ We continued refining the nullable reference types feature set with the aim of p
3. Opt-in mechanisms
## Aug 9, 2017
[C# Language Design Notes for Aug 9, 2017](LDM-2017-08-09.md)
We discussed how nullable reference types should work in a number of different situations.
1. Default expressions
2. Array creation
3. Struct fields
4. Unconstrained type parameters
=======
# C# Language Design Notes for 2017
Overview of meetings and agendas for 2017
## Jan 10, 2017
[C# Language Design Notes for Jan 10, 2017](LDM-2017-01-10.md)
1. Discriminated unions via "closed" types
## Jan 11, 2017
[C# Language Design Notes for Jan 11, 2017](LDM-2017-01-11.md)
1. Language aspects of [compiler intrinsics](https://github.com/dotnet/roslyn/issues/11475)
## Jan 17, 2017
[C# Language Design Notes for Jan 17, 2017](LDM-2017-01-17.md)
1. Constant pattern semantics: which equality exactly?
2. Extension methods on tuples: should tuple conversions apply?
## Jan 18, 2017
[C# Language Design Notes for Jan 18, 2017](LDM-2017-01-18.md)
1. Async streams (visit from Oren Novotny)
## Feb 21, 2017
[C# Language Design Notes for Feb 21, 2017](LDM-2017-02-21.md)
We triaged some of the [championed features](https://github.com/dotnet/csharplang/issues?q=is%3Aopen+is%3Aissue+label%3A%22Proposal+champion%22), to give them a tentative milestone and ensure they had a champion.
As part of this we revisited potential 7.1 features and pushed several out.
1. Implicit interface implementation in Visual Basic *(VB 16)*
2. Delegate and enum constraints *(C# X.X)*
3. Generic attributes *(C# X.0 if even practical)*
4. Replace/original *(C# X.0 if and when relevant)*
5. Bestest betterness *(C# 7.X)*
6. Null-coalescing assignments and awaits *(C# 7.X)*
7. Deconstruction in from and let clauses *(C# 7.X)*
8. Target-typed `new` expressions *(C# 7.X)*
9. Mixing fresh and existing variables in deconstruction *(C# 7.1)*
10. Implementing `==` and `!=` on tuple types *(C# 7.X)*
11. Declarations in embedded statements *(No)*
12. Field targeted attributes on auto-properties *(C# 7.1)*
## Feb 22, 2017
[C# Language Design Notes for Feb 22, 2017](LDM-2017-02-22.md)
We went over the proposal for `ref readonly`: [Champion "Readonly ref"](https://github.com/dotnet/csharplang/issues/38).
## Feb 28, 2017
[C# Language Design Notes for Feb 28, 2017](LDM-2017-02-28.md)
1. Conditional operator over refs (*Yes, but no decision on syntax*)
2. Async Main (*Allow Task-returning Main methods*)
## Mar 1, 2017
[C# Language Design Notes for Mar 1, 2017](LDM-2017-03-01.md)
1. Shapes and extensions (*exploration*)
2. Conditional refs (*original design adopted*)
## Mar 7, 2017
[C# Language Design Notes for Mar 7, 2017](LDM-2017-03-07.md)
We continued to flesh out the designs for features currently considered for C# 7.1.
1. Default expressions (*design adopted*)
2. Field target on auto-properties (*yes*)
3. private protected (*yes, if things work as expected*)
## Mar 8, 2017
[C# Language Design Notes for Mar 8, 2017](LDM-2017-03-08.md)
We looked at default interface member implementations.
1. Xamarin interop scenario
2. Proposal
3. Inheritance from interface to class
4. Overriding and base calls
5. The diamond problem
6. Binary compatibility
7. Other semantic challenges
## Mar 15, 2017
[C# Language Design Notes for Mar 8, 2017](LDM-2017-03-15.md)
Triage of championed features
1. JSON literals
2. Fixing of captured locals
3. Allow shadowing of parameters
4. Weak delegates
5. Protocols/duck typing/concepts/type classes
6. Zero and one element tuples
7. Deconstruction in lambda parameters
8. Private protected
## Mar 21, 2017
[C# Language Design Notes for Mar 21, 2017](LDM-2017-03-21.md)
Discussion of default interface member implementations, based on [this guided tour](https://github.com/dotnet/csharplang/issues/288).
1. Concerns raised on GitHub and elsewhere
2. Inheritance?
3. Breaking name lookup on `this`
4. Events
5. Modifiers
6. Methods
7. Properties
8. Overrides
9. Reabstraction
10. Most specific override
11. Static non-virtual members
12. Accessibility levels
13. Existing programs
## Mar 28, 2017
[C# Language Design Notes for Mar 28, 2017](LDM-2017-03-28.md)
Design some remaining 7.1 features
1. Fix pattern matching restriction with generics
2. Better best common type
## Mar 29, 2017
[C# Language Design Notes for Mar 29, 2017](LDM-2017-03-29.md)
1. Nullable scenarios
2. `Span<T>` safety
## Apr 5, 2017
[C# Language Design Notes for Apr 5, 2017](LDM-2017-04-05.md)
1. Non-virtual members in interfaces
2. Inferred tuple element names
3. Tuple element names in generic constraints
## Apr 11, 2017
[C# Language Design Notes for Apr 11, 2017](LDM-2017-04-11.md)
1. Runtime behavior of ambiguous default implementation
## Apr 18, 2017
[C# Language Design Notes for Apr 18, 2017](LDM-2017-04-18.md)
1. Default implementations for event accessors in interfaces
2. Reabstraction in a class of default-implemented member
3. `sealed override` with default implementations
4. Use of `sealed` keyword for non-virtual interface members
5. Implementing inaccessible interface members
6. Implicitly implementing non-public interface members
7. Not quite implementing a member
8. asynchronous `Main`
## Apr 19, 2017
[C# Language Design Notes for Apr 19, 2017](LDM-2017-04-19.md)
1. Improved best common type
2. Diamonds with classes
3. Structs and default implementations
4. Base invocation
## May 16, 2017
[C# Language Design Notes for May 16, 2017](LDM-2017-05-16.md)
1. Triage C# 7.1 features that didn't make it
2. Look at C# 7.2 features
3. GitHub procedure around new design notes and proposals
4. Triage of championed features
## May 17, 2017
[C# Language Design Notes for May 17, 2017](LDM-2017-05-17.md)
More questions about default interface member implementations
1. Conflicting override of default implementations
2. Can the Main entry point method be in an interface?
3. Static constructors in interfaces?
4. Virtual properties with private accessors
5. Does an override introduce a member?
6. Parameter names
## May 26, 2017
[C# Language Design Notes for May 26, 2017](LDM-2017-05-26.md)
1. Native ints
## May 31, 2017
[C# Language Design Notes for May 31, 2017](LDM-2017-05-31.md)
1. Default interface members: overriding or implementing?
2. Downlevel poisoning of ref readonly in signatures
3. Extension methods with ref this and generics
4. Default in operators
## Jun 13, 2017
[C# Language Design Notes for Jun 13, 2017](LDM-2017-06-13.md)
1. Native-size ints
2. Native-size floats
## Jun 14, 2017
[C# Language Design Notes for Jun 14, 2017](LDM-2017-06-14.md)
Several issues related to default implementations of interface members
1. Virtual properties with private accessors
2. Requiring interfaces to have a most specific implementation of all members
3. Member declaration syntax revisited
4. Base calls
## Jun 27, 2017
[C# Language Design Notes for Jun 27, 2017](LDM-2017-06-27.md)
1. User-defined operators in interfaces
2. return/break/continue as expressions
## Jun 28, 2017
[C# Language Design Notes for Jun 28, 2017](LDM-2017-06-28.md)
1. Tuple name round-tripping between C# 6.0 and C# 7.0
2. Deconstruction without `ValueTuple`
3. Non-trailing named arguments
## Jul 5, 2017
[C# Language Design Notes for Jul 5, 2017](LDM-2017-07-05.md)
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?
1. Static delegates *(8.0)*
2. Native int and IntPtr operators *(7.X)*
3. Field target *(anytime)*
4. Utf8 strings *(8.0)*
5. Slicing *(7.X)*
6. Blittable *(7.2)*
7. Ref structs *(7.2)*
8. Ref readonly *(7.2)*
9. Conditional ref *(7.2)*
10. Ref extensions on structs *(7.2)*
11. Readonly locals and params *(X.X)*
12. ref structs in tuples *(don't)*
13. Overload resolution tie breakers with long tuples *(use underlying generics)*
## Jul 26, 2017
[C# Language Design Notes for Jul 24 and 26, 2017](LDM-2017-07-26.md)
We started putting a series of stakes in the ground for nullable reference types, based on the evolving strawman proposal [here](https://github.com/dotnet/csharplang/issues/790). We're doing our first implementation of the feature based on this, and can then refine as we learn things from usage.
1. Goals
2. Nullable reference types
3. Rarely-null members
## Aug 7, 2017
[C# Language Design Notes for Aug 7, 2017](LDM-2017-08-07.md)
We continued refining the nullable reference types feature set with the aim of producing a public prototype for experimentation and learning.
1. Warnings
2. Local variables revisited
3. Opt-in mechanisms
## Aug 9, 2017
[C# Language Design Notes for Aug 9, 2017](LDM-2017-08-09.md)

View file

@ -94,7 +94,7 @@ namespace System.Collections.Generic
IAsyncEnumerator<T> GetAsyncEnumerator();
}
public interface IAsyncEnumerator<out T>
public interface IAsyncEnumerator<out T> : IAsyncDisposable
{
Task<bool> WaitForNextAsync();
T TryGetNext(out bool success);
@ -103,16 +103,20 @@ namespace System.Collections.Generic
```
TryGetNext is used in an inner loop to consume items with a single interface call as long as they're available synchronously. When the next item can't be retrieved synchronously, it returns false, and any time it returns false, a caller must subsequently invoke WaitForNextAsync to either wait for the next item to be available or to determine that there will never be another item. Typical consumption (without additional language features) would look like:
```C#
IAsyncEnumerable<T> enumerator = enumerable.GetAsyncEnumerator();
while (await enumerator.WaitForNextAsync())
IAsyncEnumerator<T> enumerator = enumerable.GetAsyncEnumerator();
try
{
while (true)
while (await enumerator.WaitForNextAsync())
{
int item = enumerator.TryGetNext(out bool success);
if (!success) break;
Use(item);
while (true)
{
int item = enumerator.TryGetNext(out bool success);
if (!success) break;
Use(item);
}
}
}
finally { await enumerator.DisposeAsync(); }
```
Consumption of this interface is obviously more complex. However, the advantage of this is two-fold, one minor and one major:
- _Minor: Allows for an enumerator to support multiple consumers_. There may be scenarios where it's valuable for an enumerator to support multiple concurrent consumers. That can't be achieved when `MoveNextAsync` and `Current` are separate such that an implementation can't make their usage atomic. In contrast, this approach provides a single method `TryGetNext` that supports pushing the enumerator forward and getting the next item, so the enumerator can enable atomicity if desired. However, it's likely that such scenarios could also be enabled by giving each consumer its own enumerator from a shared enumerable. Further, we don't want to enforce that every enumerator support concurrent usage, as that would add non-trivial overheads to the majority case that doesn't require it, which means a consumer of the interface generally couldn't rely on this any way.
@ -773,4 +777,4 @@ or to enabling `await` to be used directly in expressions, such as by supporting
## Integration with other asynchronous frameworks
Integration with `IObservable<T>` and other asynchronous frameworks (e.g. reactive streams) would be done at the library level rather than at the language level. For example, all of the data from an `IAsyncEnumerator<T>` can be published to an `IObserver<T>` simply by `foreach`'ing over the enumerator and `OnNext`'ing the data to the observer, so an `AsObservable<T>` extension method is possible. Consuming an `IObservable<T>` in a `foreach await` requires buffering the data (in case another item is pushed while the previous item is still being processing), but such a push-pull adapter can easily be implemented to enable an `IObservable<T>` to be pulled from with an `IAsyncEnumerator<T>`. Etc. Rx/Ix already provide prototypes of such implementations, and libraries like https://github.com/dotnet/corefxlab/tree/master/src/System.Threading.Tasks.Channels provide various kinds of buffering data structures as well as exposing both observable and async enumerable-based production and consumption. The language need not be involved at this stage.
Integration with `IObservable<T>` and other asynchronous frameworks (e.g. reactive streams) would be done at the library level rather than at the language level. For example, all of the data from an `IAsyncEnumerator<T>` can be published to an `IObserver<T>` simply by `foreach`'ing over the enumerator and `OnNext`'ing the data to the observer, so an `AsObservable<T>` extension method is possible. Consuming an `IObservable<T>` in a `foreach await` requires buffering the data (in case another item is pushed while the previous item is still being processing), but such a push-pull adapter can easily be implemented to enable an `IObservable<T>` to be pulled from with an `IAsyncEnumerator<T>`. Etc. Rx/Ix already provide prototypes of such implementations, and libraries like https://github.com/dotnet/corefxlab/tree/master/src/System.Threading.Tasks.Channels provide various kinds of buffering data structures as well as exposing both observable and async enumerable-based production and consumption. The language need not be involved at this stage.

View file

@ -152,7 +152,7 @@ There should always be an expression corresponding to the `this` parameter. Even
- Like the other `Caller*` attributes, such as `CallerMemberName`, this attribute may only be used on parameters with default values.
- Multiple parameters marked with `CallerArgumentExpression` are permitted, as shown above.
- The attribute's namespace will be `System.Runtime.CompilerServices`.
- In the case of `null` or a string that does not correspond to a parameter name (e.g. `"condition"`) is provided, an error will be raised during compilation.
- If `null` or a string that is not a parameter name (e.g. `"notAParameterName"`) is provided, the compiler will pass in an empty string.
## Drawbacks
[drawbacks]: #drawbacks
@ -247,7 +247,7 @@ There are a few disadvantages of this approach:
- Despite being pay-for-play friendly by allowing you to specify which properties you need, it could still hurt perf significantly by allocating an array for the expressions/calling `MethodBase.GetCurrentMethod` even when the assert passes.
- Additionally, while passing a new flag to the `CallerInfo` attribute won't be a breaking change, `Debug.Assert` won't be guaranteed to actually receive that new parameter from call sites that compiled against an old verion of the method.
- Additionally, while passing a new flag to the `CallerInfo` attribute won't be a breaking change, `Debug.Assert` won't be guaranteed to actually receive that new parameter from call sites that compiled against an old version of the method.
## Unresolved questions
[unresolved]: #unresolved-questions

View file

@ -7,7 +7,7 @@ In a number of common cases, this feature allows the tuple element names to be o
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".
This is particularily handy when using tuples in LINQ:
This is particularly handy when using tuples in LINQ:
```
// "c" and "result" have element names "f1" and "f2"

View file

@ -61,3 +61,4 @@ There are a couple of alternatives to consider:
- [LDM 3/7/2017](https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-03-07.md)
- [LDM 3/28/2017](https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-03-28.md)
- [LDM 5/31/2017](https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-05-31.md#default-in-operators)

View file

@ -2,33 +2,49 @@
* [x] Proposed
* [x] Prototype
* [ ] Implementation: Started
* [x] Implementation: Started
* [ ] Specification: Not Started
## Summary
[summary]: #summary
The "readonly references" feature is actually a group of features that leverage the efficiency of passing variables by reference, but without exposing the data to modifications.
The "readonly references" feature is actually a group of features that leverage the efficiency of passing variables by reference, but without exposing the data to modifications:
- `in` parameters
- `ref readonly` returns
- `readonly` structs
- `ref`/`in` extension methods
- `ref readonly` locals
- `ref` conditional expressions
# Readonly ref parameters. (aka `in` parameters)
# Passing arguments as readonly references.
There is an existing proposal that touches this topic https://github.com/dotnet/roslyn/issues/115 as a special case of readonly parameters without going into many details.
There is an existing proposal that touches this topic https://github.com/dotnet/roslyn/issues/115 as a special case of readonly parameters without going into many details.
Here I just want to acknowledge that the idea by itself is not very new.
## Motivation
C# lacks an efficient way of expressing a desire to pass struct variables into method calls for readonly purposes with no intention of modifying. Regular by-value argument passing implies copying, which adds unnecessary costs. That drives users to use by-ref argument passing and rely on comments/documentation to indicate that the data is not supposed to be mutated by the callee. It is not a good solution for many reasons.
Prior to this feature C# did not have an efficient way of expressing a desire to pass struct variables into method calls for readonly purposes with no intention of modifying. Regular by-value argument passing implies copying, which adds unnecessary costs. That drives users to use by-ref argument passing and rely on comments/documentation to indicate that the data is not supposed to be mutated by the callee. It is not a good solution for many reasons.
The examples are numerous - vector/matrix math operators in graphics libraries like [XNA](https://msdn.microsoft.com/en-us/library/bb194944.aspx) are known to have ref operands purely because of performance considerations. There is code in Roslyn compiler itself that uses structs to avoid allocations and then passes them by reference to avoid copying costs.
## Solution (`in` parameters).
## Solution
Similarly to the `out` parameters, `in` parameters are passed as managed references with additional guarantees from the callee.
Unlike `out` parameters which _must_ be assigned by the callee before any other use, `in` parameters cannot be assigned by the callee at all.
`ref readonly` parameters.
Similarly to the `out` parameters, `ref readonly` parameters are passed as managed references with additional guarantee from the callee. The guarantee in this case is that callee will not make any assignments through the parameter nor it will create a writeable reference aliases to the data referenced by the parameter.
As a result `in` parameters allow for effectiveness of indirect argument passing without exposing arguments to mutations by the callee.
## Declaring `in` parameters
`in` parameters are declared by using `in` keyword as a modifier in the parameter signature.
For all purposes the `in` parameter is treated as a `readonly` variable. Most of the restrictions on the use of `in` parameters inside the method are the same as with `readonly` fields.
> Indeed an `in` parameter may represent a `readonly` field. Similarity of restrictions is not a coincidence.
For example fields of an `in` parameter which has a struct type are all recursively classified as `readonly` variables .
```C#
static Vector3 Add (ref readonly Vector3 v1, ref readonly Vector3 v2)
static Vector3 Add (in Vector3 v1, in Vector3 v2)
{
// not OK!!
v1 = default(Vector3);
@ -40,105 +56,162 @@ static Vector3 Add (ref readonly Vector3 v1, ref readonly Vector3 v2)
foo(ref v1.X);
// OK
return new Vector3(v1.X +v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
return new Vector3(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}
```
## Syntax
- `in` parameters are allowed anywhere where ordinary byval parameters are allowed. This includes indexers, operators (including conversions), delegates, lambdas, local functions.
One of proposed syntaxes is to use existing `in` keyword as a shorter form of `ref readonly`. It could be that both syntaxes are allowed or that we pick just one of them.
> ```C#
> (in int x) => x // lambda expression
> TValue this[in TKey index]; // indexer
> public static Vector3 operator +(in Vector3 x, in Vector3 y) => ... // operator
> ```
While syntax is TBD, I will use `in` as it is significantly shorter than `ref readonly`.
- `in` is not allowed in combination with `out` or with anything that `out` does not combine with.
```C#
static Vector3 Add (in Vector3 v1, in Vector3 v2)
{
// OK
return new Vector3(v1.X +v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}
```
- It is not permitted to overload on `ref`/`out`/`in` differences.
## Use of `in` in signatures.
- It is permitted to overload on ordinary byval and `in` differences.
`in` will be allowed everywhere where `out` is allowed - methods, delegates, lambdas...
(What about indexers, operators? - TBD)
- For the purpose of OHI (Overloading, Hiding, Implementing), `in` behaves similarly to an `out` parameter.
All the same rules apply.
For example the overriding method will have to match `in` parameters with `in` parameters of an identity-convertible type.
`in` would not be allowed in combination with `out` or with anything that `out` does not combine with.
For the purpose of OHI (Overloading, Hiding, Implementing), `in` will behave similarly to an `out` parameter. All the same rules apply, in particular it is not permitted to overload on `ref/out/in` differences.
- For the purpose of delegate/lambda/method group conversions, `in` behaves similarly to an `out` parameter.
Lambdas and applicable method group conversion candidates will have to match `in` parameters of the target delegate with `in` parameters of an identity-convertible type.
For the purpose of binding and overload resolution `in` behaves similarly to `out` as well. `in` is basically just a new element of `RefKind` enum, in addition to current `{None, Ref, Out}`.
- For the purpose of generic variance, `in` parameters are nonvariant.
For the purpose of generic variance, `in` is nonvariant.
> NOTE: There are no warnings on `in` parameters that have reference or primitives types.
It may be pointless in general, but in some cases user must/want to pass primitives as `in`. Examples - overriding a generic method like `Method(in T param)` when `T` was substituted to be `int`, or when having methods like `Volatile.Read(in int location)`
>
> It is conceivable to have an analyzer that warns in cases of inefficient use of `in` parameters, but the rules for such analysis would be too fuzzy to be a part of a language specification.
## Use of `in` at call sites.
## Use of `in` at call sites. (`in` arguments)
Unlike `out` parameters, `in` does not need to be matched with an LValue(*) argument. At the call-site `in` parameters are no different from regular by-value parameters.
There are two ways to pass arguments to `in` parameters.
Example:
Notice that there are no `in` modifiers at the call.
Also note that passing an RValue(*) is acceptable.
### `in` arguments can match `in` parameters:
An argument with an `in` modifier at the call site can match `in` parameters.
```C#
static Vector3 ShiftRightTwice(in Vector3 v1)
int x = 1;
void M1<T>(in T x)
{
// + UnitX twice
return Add(Add(v1, Vector3.UnitX), Vector3.UnitX);
. . .
}
static Vector3 Add (in Vector3 v1, in Vector3 v2)
var x = M1(in x); // in argument to a method
class D
{
// OK
return new Vector3(v1.X +v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
public string this[in Guid index];
}
D dictionary = . . . ;
var y = dictionary[in Guid.Empty]; // in argument to an indexer
```
In cases of variables without a home a temporary variable will be used for a short term capturing of the value.
- `in` argument must be a _readable_ LValue(*).
Example: `M1(in 42)` is invalid
`in` parameters are not writeable and rules similar to readonly fields apply. - I.E. fields of a readonly ref parameter of a struct type are recursively not writeable as well.
It is permitted to use `in` parameters for passing further or recursively as `in` parameters, or for other `ref readonly` purposes as discussed further.
(*) The notion of [LValue/RValue](https://en.wikipedia.org/wiki/Value_(computer_science)#lrvalue) vary between languages.
Here, by LValue I mean an expression that can be assigned to or passed by reference.
> (*) The notion of [LValue/RValue](https://en.wikipedia.org/wiki/Value_(computer_science)#lrvalue) vary between languages.
Here, by LValue I mean an expression that represent a location that can be referred to directly.
And RValue means an expression that yields a temporary result which does not persist on its own.
- In particular it is valid to pass `readonly` fields, `in` parameters or other formally `readonly` variables as `in` arguments.
Example: `dictionary[in Guid.Empty]` is legal. `Guid.Empty` is a static readonly field.
## Passing `readonly` fields as `in` parameters.
- `in` argument must have type _identity-convertible_ to the type of the parameter.
Example: `M1<object>(in Guid.Empty)` is invalid. `Guid.Empty` is not _identity-convertible_ to `object`
When passing `readonly` fields (outside of a ctor) we have two choices - pass through a local copy or pass a direct reference. For the purpose of just `in` parameters the difference is semantically unobservable. However the direct reference is more efficient. We will work with CLR to allow the latter.
The motivation for the above rules is that `in` arguments guarantee _aliasing_ of the argument variable. The callee always receives a direct reference to the same location as represented by the argument.
NOTE: adding `ref readonly` _returns_ as a feature makes copying of `ref readonlys` at the call sites observable, thus further cementing the requirement that `readonly` fields are _not_ passed by copy.
- in rare situations when `in` arguments must be stack-spilled due to `await` expressions used as operands of the same call, the behavior is the same as with `out` and `ref` arguments - if the variable cannot be spilled in referentially-transparent manner, an error is reported.
## `in` parameters and capturing of stack variables.
Examples:
1) `M1(in staticField, await SomethingAsync())` is valid.
`staticField` is a static field which can be accessed more than once without observable side effects. Therefore both the order of side effects and aliasing requirements can be provided.
For the purpose of lambda/async capturing `in` will behave the same as `out` - i.e. no capturing allowed.
We can, alternatively, allow capturing of copied temps (by ref or by value), but that would break the aliasing behavior of `in` and would result in subtle changes in behavior based on whether variables are captured or not, which by itself is not a stable guarantee and may depend on various factors including debug vs. release. Besides, copying would defeat the purpose of the feature that is to reduce copying. As such it is advisable to not allow capturing of `in` parameters.
2) `M1(in RefReturningMethod(), await SomethingAsync())` will produce an error.
`RefReturningMethod()` is a `ref` returning method. A method call may have observable side effects, therefore it must be evaluated before the `SomethingAsync()` operand. However the result of the invocation is a reference that cannot be preserved across the `await` suspension point which make the direct reference requirement impossible.
Example of observable aliasing/copying if capture via copying would be allowed:
> NOTE: the stack spilling errors are considered to be implementation-specific limitations. Therefore they do not have effect on overload resolution or lambda inference.
### Ordinary byval arguments can match `in` parameters:
Regular arguments without modifiers can match `in` parameters. In such case the arguments have the same relaxed constraints as an ordinary byval arguments would have.
The motivation for this scenario is that `in` parameters in APIs may result in inconveniences for the user when arguments cannot be passed as a direct reference - ex: literals, computed or `await`-ed results or arguments that happen to have more specific types.
All these cases have a trivial solution of storing the argument value in a temporary local of appropriate type and passing that local as an `in` argument.
To reduce the need for such boilerplate code compiler can perform the same transformation, if needed, when `in` modifier is not present at the call site.
In addition, in some cases, such as invocation of operators, or `in` extension methods, there is no syntactical way to specify `in` at all. That alone requires specifying the behavior of ordinary byval arguments when they match `in` parameters.
In particular:
- it is valid to pass RValues.
A reference to a temporary is passed in such case.
Example:
```C#
static Vector3 v;
Print("hello"); // not an error.
static void Main()
void Print<T>(in T x)
{
Test(v);
}
static void Test(in Vector3 v1)
{
v = Vector3.UnitX;
Debug.Assert(v1 == Vector3.UnitX);
// uncomment this to see behavior of code above change
// Func<Vector3> f = () => v1;
. . .
}
```
## Aliasing behavior in general
- implicit conversions are allowed.
Just like `out` variables, `in` variables are aliases.
While callee is not allowed to write into them, there should not be an assumption that they cannot change (deterministically or not) between reads. As such creating a copy of an `in` parameter can be observable.
> This is actually a special case of passing an RValue
A reference to a temporary holding converted value is passed in such case.
Example:
```C#
Print<int>(Short.MaxValue) // not an error.
```
- in a case of a receiver of an `in` extension method (as opposed to `ref` extension methods), RValues or implicit _this-argument-conversions_ are allowed.
A reference to a temporary holding converted value is passed in such case.
Example:
```C#
public static IEnumerable<T> Concat<T>(in this (IEnumerable<T>, IEnumerable<T>) arg) => . . .;
("aa", "bb").Concat<char>() // not an error.
```
More information on `ref`/`in` extension methods is provided further in this document.
- argument spilling due to `await` operands could spill "by-value", if necessary.
In scenarios where providing a direct reference to the argument is not possible due to intervening `await` a copy of the argument's value is spilled instead.
Example:
```C#
M1(RefReturningMethod(), await SomethingAsync()) // not an error.
```
Since the result of a side-effecting invocation is a reference that cannot be preserved across `await` suspension, a temporary containing the actual value will be preserved instead (as it would in an ordinary byval parameter case).
### Omitted optional arguments.
It is permitted for an `in` parameter to specify a default value. That makes the corresponding argument optional.
Omitting optional argument at the call site results in passing the default value via a temporary.
```C#
Print("hello"); // not an error, same as
Print("hello", c: Color.Black);
void Print(string s, in Color c = Color.Black)
{
. . .
}
```
## Aliasing behavior in general
Just like `ref` and `out` variables, `in` variables are references/aliases to existing locations.
While callee is not allowed to write into them, reading an `in` parameter can observe different values as a side effect of other evaluations.
Example:
@ -164,137 +237,122 @@ static void ChangeV()
}
```
## Conversions at the call site.
## `in` parameters and capturing of local variables.
For the purpose of lambda/async capturing `in` parameters behave the same as `out` and `ref` parameters.
Aliasing indirectly affects what conversions are allowed at the call-site.
While explicit and identity conversions are ok, it is not clear whether implicit conversions are ok since they only can be allowed via implicit copying.
For starters, let's assume implicit conversions are _not_ ok, but may consider alternatives.
- `in` parameters cannot be captured in a closure
- `in` parameters are not allowed in iterator methods
- `in` parameters are not allowed in async methods
Not allowing implicit conversions would at least be consistent with `out` parameters, although less intuitive since `in` is not spelled out at the call site. It would also be simpler in terms of impact on overload resolution and betterness analysis.
## Temporary variables.
Some uses of `in` parameter passing may require indirect use of a temporary local variable:
- `in` arguments are always passed as direct aliases when call-site uses `in`. Temporary is never used in such case.
- `in` arguments are not required to be direct aliases when call-site does not use `in`. When argument is not an LValue, a temporary may be used.
- `in` parameter may have default value. When corresponding argument is omitted at the call site, the default value are passed via a temporary.
- `in` arguments may have implicit conversions, including those that do not preserve identity. A temporary is used in those cases.
- receivers of ordinary struct calls may not be writeable LValues (**existing case!**). A temporary is used in those cases.
## Invoking instance struct methods on `in` parameters.
Since all regular instance methods of a struct can potentially mutate the instance or ref-expose `this`, an intermediate copy must be created, as already a case when receiver is a readonly field.
The life time of the argument temporaries matches the closest encompassing scope of the call-site.
However, since there is no backward compatibility considerations and there are workarounds, compiler should give a warning to ensure the implicit copying is noted by the user.
The formal life time of temporary variables is semantically significant in scenarios involving escape analysis of variables returned by reference.
## Metadata representaion.
## Metadata representation of `in` parameters.
When `System.Runtime.CompilerServices.IsReadOnlyAttribute` is applied to a byref parameter, it means that the the parameter is an `in` parameter.
We're using a combination of attributes and modifiers on signatures:
In addition, if the method is *abstract* or *virtual*, then the signature of such parameters (and only such parameters) must have `modreq[System.Runtime.CompilerServices.IsReadOnlyAttribute]`.
1) `IsReadOnlyAttribute` is added to the framework, to be used on parameters and return types, to indicate that they are `ref readonly`. For older frameworks, the compiler will generate an embedded (hidden) attribute with the same name into the module being built, and we will have a mechanism for disallowing using this attribute in source code.
2) We will use a `modreq` with the framework type `IsConst`, to prevent other compilers and older versions of the C# compiler to read such signatures. This might not be needed on some cases like parameters of non-virtual methods, as downlevel use is safe. For tools and other compilers reading these metadata, they should always rely on the attribute, as it is mandatory.
**Motivation**: this is done to ensure that in a case of method overriding/implementing the `in` parameters match.
# Readonly ref returns
Same requirements apply to `Invoke` methods in delegates.
"Readonly ref returns" is an augmentation of ref returns that allows returning references to variables without exposing them to modification.
**Motivation**: this is to ensure that existing compilers cannot simply ignore `readonly` when creating or assigning delegates.
# Readonly ref locals
Locals are not supported for now. No plans to support them in this proposal.
# Returning by readonly reference.
## Motivation
The motivation for this sub-feature is roughly symmetrical to the reasons for the `in` parameters - avoiding copying, but on the returning side. A method or an indexer of a nontrivial struct type has currently two options -return by reference and be exposed to possible mutations or copy the value.
The motivation for this sub-feature is roughly symmetrical to the reasons for the `in` parameters - avoiding copying, but on the returning side. Prior to this feature, a method or an indexer had two options: 1) return by reference and be exposed to possible mutations or 2) return by value which results in copying.
## Solution
`ref readonly` _returns_. (a more concise modifier like `in` so far has been elusive for this case).
## Solution (`ref readonly` returns)
The feature allows a member to return variables by reference without exposing them to mutations.
## Declaring `ref readonly` returning members
A combination of modifiers `ref readonly` on the return signature is used to to indicate that the member returns a readonly reference.
For all purposes a `ref readonly` member is treated as a `readonly` variable - similar to `readonly` fields and `in` parameters.
For example fields of `ref readonly` member which has a struct type are all recursively classified as `readonly` variables. - It is permitted to pass them as `in` arguments, but not as `ref` or `out` arguments.
```C#
ref readonly Guid Method1()
{
}
Method2(in Method1()); // valid. Can pass as `in` argument.
Method3(ref Method1()); // not valid. Cannot pass as `ref` argument
```
- `ref readonly` returns are allowed in the same places were `ref` returns are allowed.
This includes indexers, delegates, lambdas, local functions.
- It is not permitted to overload on `ref`/`ref readonly` / differences.
- It is permitted to overload on ordinary byval and `ref readonly` return differences.
- For the purpose of OHI (Overloading, Hiding, Implementing), `ref readonly` is similar but distinct from `ref`.
For example the a method that overrides `ref readonly` one, must itself be `ref readonly` and have identity-convertible type.
- For the purpose of delegate/lambda/method group conversions, `ref readonly` is similar but distinct from `ref`.
Lambdas and applicable method group conversion candidates have to match `ref readonly` return of the target delegate with `ref readonly` return of the type that is identity-convertible.
- For the purpose of generic variance, `ref readonly` returns are nonvariant.
> NOTE: There are no warnings on `ref readonly` returns that have reference or primitives types.
It may be pointless in general, but in some cases user must/want to pass primitives as `in`. Examples - overriding a generic method like `ref readonly T Method()` when `T` was substituted to be `int`.
>
>It is conceivable to have an analyzer that warns in cases of inefficient use of `ref readonly` returns, but the rules for such analysis would be too fuzzy to be a part of a language specification.
## Returning from `ref readonly` members
Inside the method body the syntax is the same as with regular ref returns. The `readonly` will be inferred from the containing method.
The motivation is that `return ref readonly <expression>` is unnecessary long and only allows for mismatches on the `readonly` part that would always result in errors.
The `ref` is, however, required for consistency with other scenarios where something is passed via strict aliasing vs. by value.
> Unlike the case with `in` parameters, `ref readonly` returns never return via a local copy. Considering that the copy would cease to exist immediately upon returning such practice would be pointless and dangerous. Therefore `ref readonly` returns are always direct references.
Example:
```C#
struct ImmutableArray<T>
{
private readonly T[] array;
public ref readonly T RefAt(int i)
public ref readonly T ItemRef(int i)
{
// returning a ref readonly
// returning a readonly reference to an array element
return ref this.r1;
}
}
```
`readonly` on the ref return will prevent the caller from using the result for indirect writing or for obtaining writeable references.
## Syntax
`ref readonly` will be used to modify member signatures to indicate the return is passed as a readonly ref.
It seems unnecessary to use `readonly` in the return statement. Just `ref` would be sufficient.
We could require the whole `return ref readonly foo`, but it seems it would only add the requirement that all the returns within a given method must agree on the `readonly` part and agree with the signature of the member. Omitting `readonly` at the return site makes `readonly` implicit on all returns in a method/lambda.
We should, however, require `ref` for consistency with other scenarios where something is passed via an alias vs. by value.
## Use of `ref readonly' returns in signatures
`ref readonly` will be allowed in the same places were `ref` returns are allowed. For all the OHI purposes it will behave as another RefKind on the return. I.E. `readonly` would need to match exactly when overriding, it will not be possible to overload just on `readonly` difference, etc...
From the point of implementation it would be essentially the same RefKind as for `in` parameters applicable to returns as well.
For the purpose of variance, readonly ref will work as non-variant.
## Returning `readonly` fields as `ref readonly`s.
Unlike the requirements of `in`, where pass-by-copy is possible, while not the most efficient, returning a `readonly` field as a `ref readonly` requires that it is a true reference.
In particular, the requirements of the ref returns have indirect effect on requirements of `in`. As long as `in` parameters and `readonly` fields are both returnable, the copying would be:
1. observable and
2. would go against the goals of the feature to reduce copying.
Here is an ugly, but legal and very explicit example where copying would be observable:
```C#
class Program
{
private readonly Vector3 v = Vector3.UnitY;
public Program()
{
CompareFirstLast(
FetchRef(),
v = Vector3.UnitX, // can assign since we are in a ctor
v, // making a copy here would be observable
v = Vector3.UnitZ); // can assign since we are in a ctor
}
ref readonly Vector3 FetchRef()
{
// making a copy here would be observable
return ref v;
}
bool CompareFirstLast(
in Vector3 first,
Vector3 dummy1,
in Vector3 last,
Vector3 dummy2)
{
// should be the same value regardless of assignments
// since these are refs to the same variable
Debug.Assert(first == last);
}
}
```
Note: Since CLR does not differentiate readonly and writeable references. The distinction may not be required for general scenarios like JIT-compilation. However, some work will be needed to introduce `ref readonly` notion within the Verification infrastructure. At least if we want passing references to `readonly` fields be formally verifiable.
## Aliasing behavior.
A care must be made to preserve the aliasing behavior and not allow capturing by-value. As a result, readonly refs should be treated the same as other refs for the purpose of capturing in lambdas, async, iterators, stack spilling etc... - I.E. most scenarios would be disallowed.
It would be ok to make a copy when `ref readonly` return is a receiver of regular struct methods, which take `this` as an ordinary writeable refs. Historically we would make an invocation on a copy.
In theory we could produce a warning when making a copy of the receiver since there are no backward compat considerations here. The options for fixing the warning are limited though, so it is debatable whether a warning is desirable.
- An argument of `return ref` must be an LValue (**existing rule**)
- An argument of `return ref` must be "safe to return" (**existing rule**)
- In a `ref readonly` member an argument of `return ref` is _not required to be writeable_ .
For example such member can ref-return a readonly field or one of its `in` parameters.
## Safe to Return rules.
Normal safe to return rules for references will apply to readonly references as well.
Normal safe to return rules for references will apply to readonly references as well.
Note that a `ref readonly` can be obtained from a regular `ref` local/parameter/return, but not the other way around. Otherwise the safety of `ref readonly` returns is inferred the same way as for regular `ref` returns.
Note that a `ref readonly` can be obtained through a regular ref, but not the other way around. Otherwise the safety of `ref readonly`s is inferred the same way as for the regular refs.
Considering that RValues can be passed as `in` parameter we need one more rule - **RValues are not safe-to-return by reference**.
We also must consider the situation of RValues passed as `in` parameters via a copy and then coming back in a form of a `ref readonly` and thus the result of the invocation is clearly unsafe to return.
Once RValues are not safe to return, the existing rule `#6` already handles this case.
Considering that RValues can be passed as `in` parameter and returned as `ref readonly` we need one more rule - **RValues are not safe-to-return by reference**.
> Consider the situation when an RValue is passed to an `in` parameter via a copy and then returned back in a form of a `ref readonly`. In the context of the caller the result of such invocation is a reference to local data and as such is unsafe to return.
> Once RValues are not safe to return, the existing rule `#6` already handles this case.
Example:
```C#
@ -324,20 +382,77 @@ Updated `safe to return` rules:
6. **a ref, returned from another method is safe to return if all refs/outs passed to that method as formal parameters were safe to return.**
*Specifically it is irrelevant if receiver is safe to return, regardless whether receiver is a struct, class or typed as a generic type parameter.*
7. **RValues are not safe to return by reference.**
*Specifically RValues are safe to pass as readonly ref / in parameters.*
*Specifically RValues are safe to pass as in parameters.*
> NOTE: There are additional rules regarding safety of returns that come into play when ref-like types and ref-reassignments are involved.
> The rules equally apply to `ref` and `ref readonly` members and therefore are not mentioned here.
A special note must be made about the life time of the temps used as an implementation detail of passing RValue as an `in` parameter. The temp must exist as long as any reference to it can possibly exist.
Considering that readonly refs cannot be persisted into regular ref locals, the duration of the encompassing expression could be sufficient. (encompassing expression is the one that is not an operand/argument/receiver to another one)
For simplicity, the extent of the temps could be widened to the nearest encompassing Block/Sequence.
## Aliasing behavior.
`ref readonly` members provide the same aliasing behavior as ordinary `ref` members (except for being readonly).
Therefore for the purpose of capturing in lambdas, async, iterators, stack spilling etc... the same restrictions apply. - I.E. due to inability to capture the actual references and due to side-effecting nature of member evaluation such scenarios are disallowed.
As a last resort the temps could be method-wide. We currently do it already in very rare cases (see comment in `EmitAssignmentValue` and testcase `IncrementPropertyOfTypeParameterReturnValue`), but I'd rather tighten the existing case since the pattern of taking a ref off an RValue by the means of spilling into a temp will now be more common.
> It is permitted and required to make a copy when `ref readonly` return is a receiver of regular struct methods, which take `this` as an ordinary writeable reference. Historically in all cases where such invocations are applied to readonly variable a local copy is made.
# ref/in extension methods
There is actually existing proposal (https://github.com/dotnet/roslyn/issues/165) and corresponding PR (https://github.com/dotnet/roslyn/pull/15650).
I just want to acknowledge that this idea is not entirely new. It is, however, relevant here since `ref readonly` elegantly removes the most contentious issue about such method - what to do with RValue receivers.
## Metadata representation.
When `System.Runtime.CompilerServices.IsReadOnlyAttribute` is applied to the return of a byref returning method, it means that the method returns a readonly reference.
The general idea of the proposal is to allow extension methods to take the `this` parameter by reference, as long as the type is known to be a struct type (I.E. struct or a generic type with `struct` constraint).
In addition, the result signature of such methods (and only those methods) must have `modreq[System.Runtime.CompilerServices.IsReadOnlyAttribute]`.
**Motivation**: this is to ensure that existing compilers cannot simply ignore `readonly` when invoking methods with `ref readonly` returns
# Readonly structs
In short - a feature that makes `this` parameter of all instance members of a struct, except for constructors, an `in` parameter.
## Motivation
Compiler must assume that any method call on a struct instance may modify the instance. Indeed a writeable reference is passed to the method as `this` parameter and fully enables this behavior. To allow such invocations on `readonly` variables, the invocations are applied to temp copies. That could be unintuitive and sometimes forces people to abandon `readonly` for performance reasons.
Example: https://codeblog.jonskeet.uk/2014/07/16/micro-optimization-the-surprising-inefficiency-of-readonly-fields/
After adding support for `in` parameters and `ref readonly` returns the problem of defensive copying will get worse since readonly variables will become more common.
## Solution
Allow `readonly` modifier on struct declarations which would result in `this` being treated as `in` parameter on all struct instance methods except for constructors.
```C#
static void Test(in Vector3 v1)
{
// no need to make a copy of v1 since Vector3 is a readonly struct
System.Console.WriteLine(v1.ToString());
}
readonly struct Vector3
{
. . .
public override string ToString()
{
// not OK!! `this` is an `in` parameter
foo(ref this.X);
// OK
return $"X: {X}, Y: {Y}, Z: {Z}";
}
}
```
## Restrictions on members of readonly struct
- Instance fields of a readonly struct must be readonly.
**Motivation:** can only be written to externally, but not through members.
- Instance autoproperties of a readonly struct must be get-only.
**Motivation:** consequence of restriction on instance fields.
- Readonly struct may not declare field-like events.
**Motivation:** consequence of restriction on instance fields.
## Metadata representation.
When `System.Runtime.CompilerServices.IsReadOnlyAttribute` is applied to a value type, it means that the the type is a `readonly struct`.
In particular:
- The identity of the `IsReadOnlyAttribute` type is unimportant. In fact it can be embedded by the compiler in the containing assembly if needed.
# `ref`/`in` extension methods
There is actually an existing proposal (https://github.com/dotnet/roslyn/issues/165) and corresponding prototype PR (https://github.com/dotnet/roslyn/pull/15650).
I just want to acknowledge that this idea is not entirely new. It is, however, relevant here since `ref readonly` elegantly removes the most contentious issue about such methods - what to do with RValue receivers.
The general idea is allowing extension methods to take the `this` parameter by reference, as long as the type is known to be a struct type.
```C#
public static void Extension(ref this Guid self)
@ -351,21 +466,22 @@ The reasons for writing such extension methods are primarily:
2. Allow mutating extension methods on structs
The reasons why we do not want to allow this on classes
1. It would be of very limited purpose
2. It would be hard to reconcile with "evaluate once" semantics of null-conditional accesses.
1. It would be of very limited purpose.
2. It would break long standing invariant that a method call cannot turn non-`null` receiver to become `null` after invocation.
> In fact, currently a non-`null` variable cannot become `null` unless _explicitly_ assigned or passed by `ref` or `out`.
> That greatly aids readability or other forms of "can this be a null here" analysis.
3. It would be hard to reconcile with "evaluate once" semantics of null-conditional accesses.
Example:
`obj.stringField?.RefExtension(...)` - need to capture a copy of `stringField` to make the null check meaningful, but then assignments to `this` inside RefExtension would not be reflected back to the field.
`obj.stringField?.RefExtension(...)` - need to make a copy of stringField for the null check, but then assignments to `this` inside RefExtension would not be reflected back to the field...
An ability to declare extension methods on **structs** that take the first argument by reference was a long-standing request. One of the blocking consideration was "what happens if receiver is not an LValue?".
An ability to declare extension methods on structs that take the first argument by reference was a long-standing request. One of the blocking consideration was "what happens if receiver is not an LValue".
- There is a precedent that any extension method could also be called as a static method (sometimes it is the only way to resolve ambiguity). It would dictate that RValue receivers should be disallowed.
- On the other hand there is a practice of making invocation on a copy in similar situations when struct instance methods are involved.
An analogy with static methods would dictate that it is disallowed, but it would not be consistent with the practice of making invocation on a copy in similar situations when instance methods are involved. In addition, many of the calls for allowing ref extension methods on structs were motivated by the performance and implicit copying would diminish the benefit.
The reason why the "implicit copying" exists is because the majority of struct methods do not actually modify the struct while not being able to indicate that. Therefore the most practical solution was to just make the invocation on a copy, but this practice is known for harming performance and causing bugs.
The reason why the "implicit copying" exists is because the majority of struct methods do not actually modify the struct while not being able to indicate that. Historically the most practical solution was to just make the invocation on a copy, but this practice is known for harming performance and causing bugs.
Now, assuming availability of `in` parameters, it feels reasonable to assume that ref extension methods are introduced specifically to apply mutations to `this` and thus require that the receiver is writeable.
In addition it would also be allowed to have `in` extension methods that would not have such restriction, while naturally would not be able to mutate `this`.
Now, with availability of `in` parameters, it is possible for an extension to signal the intent. Therefore the conundrum can be resolved by requiring `ref` extensions to be called with writeable receivers while `in` extensions permit implicit copying if necessary.
```C#
// this can be called on either RValue or an LValue
@ -383,53 +499,164 @@ public static void Mutator(ref this Guid self)
}
```
# Readonly structs
## `in` extensions and generics.
The purpose of `ref` extension methods is to mutate the receiver directly or by invoking mutating members. Therefore `ref this T` extensions are allowed as long as `T` is constrained to be a struct.
In short - a feature that makes all members of a struct, except constructors to have `this` parameter as a `ref readonly`.
On the other hand `in` extension methods exist specifically to reduce implicit copying. However any use of an `in T` parameter will have to be done through an interface member. Since all interface members are considered mutating, any such use would require a copy. - Instead of reducing copying, the effect would be the opposite. Therefore `in this T` is not allowed when `T` is a generic type parameter regardless of constraints.
## Motivation
## Valid kinds of extension methods (recap):
The following forms of `this` declaration in an extension method are now allowed:
1) `this T arg` - regular byval extension. (**existing case**)
- T can be any type, including reference types or type parameters.
Instance will be the same variable after the call.
Allows implicit conversions of _this-argument-conversion_ kind.
Can be called on RValues.
Compiler must assume that any method call on a struct instance may modify the instance. Indeed a writeable reference is passed to the method as `this` parameter that fully enables this behavior. To allow such invocations on `readonly` variables, the invocation are applied to temp copies. That could be unintuitive and sometimes forces people to abandon `readonly` for performance reasons. (Example: https://codeblog.jonskeet.uk/2014/07/16/micro-optimization-the-surprising-inefficiency-of-readonly-fields/ )
- `in this T self` - `in` extension.
T must be an actual struct type.
Instance will be the same variable after the call.
Allows implicit conversions of _this-argument-conversion_ kind.
Can be called on RValues (may be invoked on a temp if needed).
The problem will get worse since the implicit copying will be happening when invoking struct methods on `in` variables. - We can give warnings, but we must make defensive copies and that might force users into choosing between performance and more control over sideeffects.
- `ref this T self` - `ref` extension.
T must be a struct type or a generic type parameter constrained to be a struct.
Instance may be written to by the invocation.
Allows only identity conversions.
Must be called on writeable LValue. (never invoked via a temp).
## Solution
Allow `readonly` modifier on struct declarations which would result in `this` being an `in` parameter on all struct instance methods except for constructors.
# Readonly ref locals.
## Motivation.
Once `ref readonly` members were introduced, it was clear from the use that they need to be paired with appropriate kind of local. Evaluation of a member may produce or observe side effects, therefore if the result must be used more than once, it needs to be stored. Ordinary `ref` locals do not help here since they cannot be assigned a `readonly` reference.
## Solution.
Allow declaring `ref readonly` locals. This is a new kind of `ref` locals that is not writeable. As a result `ref readonly` locals can accept references to readonly variables without exposing these variables to writes.
## Declaring and using `ref readonly` locals.
The syntax of such locals uses `ref readonly` modifiers at declaration site (in that specific order). Similarly to ordinary `ref` locals, `ref readonly` locals must be ref-initialized at declaration. Unlike regular `ref` locals, `ref readonly` locals can refer to `readonly` LValues like `in` parameters, `readonly` fields, `ref readonly` methods.
For all purposes a `ref readonly` local is treated as a `readonly` variable. Most of the restrictions on the use are the same as with `readonly` fields or `in` parameters.
For example fields of an `in` parameter which has a struct type are all recursively classified as `readonly` variables .
```C#
static void Test(in Vector3 v1)
static readonly ref Vector3 M1() => . . .
static readonly ref Vector3 M1_Trace()
{
// no need to make a copy of v1 since Vector3 is a readonly struct
System.Console.WriteLine(v1.ToString());
// OK
ref readonly var r1 = ref M1();
// Not valid. Need an LValue
ref readonly Vector3 r2 = ref default(Vector3);
// Not valid. r1 is readonly.
Mutate(ref r1);
// OK.
Print(in r1);
// OK.
return ref r1;
}
```
readonly struct Vector3
## Restrictions on use of `ref readonly` locals
Except for their `readonly` nature, `ref readonly` locals behave like ordinary `ref` locals and are subject to exactly same restrictions.
For example restrictions related to capturing in closures, declaring in `async` methods or the `safe-to-return` analysis equally applies to `ref readonly` locals.
# Ternary `ref` expressions. (aka "Conditional LValues")
## Motivation
Use of `ref` and `ref readonly` locals exposed a need to ref-initialize such locals with one or another target variable based on a condition.
A typical workaround is to introduce a method like:
```C#
ref T Choice(bool condition, ref T consequence, ref T alternative)
{
. . .
public override string ToString()
if (condition)
{
// not OK!! "this" is an `in` parameter
foo(ref this.X);
// OK
return $"X: {X}, Y: {Y}, Z: {Z}";
return ref consequence;
}
else
{
return ref alternative;
}
}
```
The feature is surprisingly uncontroversial. The only obvious question is whether there is a need for an option to `opt out` some of the methods as mutators.
Note that `Choice` is not an exact replacement of a ternary since _all_ arguments must be evaluated at the call site, which was leading to unintuitive behavior and bugs.
So far it feels like per-member control over `readonly` is an unnecessary complication, which also can be added later if found to be necessary.
Current assumptions are that "Mixed" mutable/immutable structs are not common. Besides even partially mutable struct variables would generally need to be LValues and thus would not impacted by the implicit copying.
The following will not work as expected:
```C#
// will crash with NRE because 'arr[0]' will be executed unconditionally
ref var r = ref Choice(arr != null, ref arr[0], ref otherArr[0]);
```
## Solution
Allow special kind of conditional expression that evaluates to a reference to one of LValue argument based on a condition.
## Using `ref` ternary expression.
The syntax for the `ref` flavor of a conditional expression is ` <condition> ? ref <consequence> : ref <alternative>;`
Just like with the ordinary conditional expression only `<consequence>` or `<alternative>` is evaluated depending on result of the boolean condition expression.
Unlike ordinary conditional expression, `ref` conditional expression:
- requires that `<consequence>` and `<alternative>` are LValues.
- `ref` conditional expression itself is an LValue and
- `ref` conditional expression is writeable if both `<consequence>` and `<alternative>` are writeable LValues
Examples:
`ref` ternary is an LValue and as such it can be passed/assigned/returned by reference;
```C#
// pass by reference
foo(ref (arr != null ? ref arr[0]: ref otherArr[0]));
// return by reference
return ref (arr != null ? ref arr[0]: ref otherArr[0]);
```
Being an LValue, it can also be assigned to.
```C#
// assign to
(arr != null ? ref arr[0]: ref otherArr[0]) = 1;
// error. readOnlyField is readonly and thus conditional expression is readonly
(arr != null ? ref arr[0]: ref obj.readOnlyField) = 1;
```
Can be used as a receiver of a method call and skip copying if necessary.
```C#
// no copies
(arr != null ? ref arr[0]: ref otherArr[0]).StructMethod();
// invoked on a copy.
// The receiver is `readonly` because readOnlyField is readonly.
(arr != null ? ref arr[0]: ref obj.readOnlyField).StructMethod();
// no copies. `ReadonlyStructMethod` is a method on a `readonly` struct
// and can be invoked directly on a readonly receiver
(arr != null ? ref arr[0]: ref obj.readOnlyField).ReadonlyStructMethod();
```
`ref` ternary can be used in a regular (not ref) context as well.
```C#
// only an example
// a regular ternary could work here just the same
int x = (arr != null ? ref arr[0]: ref otherArr[0]);
```
## Drawbacks
[drawbacks]: #drawbacks
I can see two major arguments against:
I can see two major arguments against enhanced support for references and readonly references:
1) The problems that are solved here are very old. Why suddenly solve them now, especially since it would not help existing code?
1) The problems that are solved here are very old. Why suddenly solve them now, especially since it would not help existing code?
As we find C# and .Net used in new domains, some problems become more prominent.
As examples of environments that are more critical than average about computation overheads, I can list
@ -439,7 +666,6 @@ As examples of environments that are more critical than average about computatio
This feature does not sacrifice any of the existing strengths such as type-safety, while allowing to lower overheads in some common scenarios.
2) Can we reasonably guarantee that the callee will play by the rules when it opts into `readonly` contracts?
We have similar trust when using `out`. Incorrect implementation of `out` can cause unspecified behavior, but in reality it rarely happens.
@ -454,16 +680,10 @@ The main competing design is really "do nothing".
## Unresolved questions
[unresolved]: #unresolved-questions
* Actual syntax need to be vetted.
* Applicability beyond methods - indexers, operators . . .
* Diagnostics related to implicit copying of `this`
* Conversions at 'in' call sites.
* Need to agree on metadata representation.
* Changes to the Verification rules
* Spec 7.5.3.1 would have to change to reflect that ref extension methods arguments are now passed without using a 'ref' keyword.
## Design meetings
Link to design notes that affect this proposal, and describe in one sentence for each what changes they led to.
https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-02-22.md
https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-03-01.md
https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-08-28.md
https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-09-25.md
https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-09-27.md

View file

@ -44,7 +44,7 @@ An additional measure will be taken to prevent the use of ref-like structs in co
Having no other good alternatives that work in old compilers without servicing, an `Obsolete` attribute with a known string will be added to all ref-like structs. Compilers that know how to use ref-like types will ignore this particular form of `Obsolete`.
A typical matadata representation:
A typical metadata representation:
```C#
[IsRefLike]
@ -234,7 +234,7 @@ A stackalloc expression is an rvalue that is *safe-to-escape* to the top-level s
A `new` expression that invokes a constructor obeys the same rules as a method invocation that is considered to return the type being constructed.
In addition *safe-to-escape* is no wider than the smallest of the *safe-to-escape* of all arguments/operands of the object initializer expressons, recursively, if initializer is present.
In addition *safe-to-escape* is no wider than the smallest of the *safe-to-escape* of all arguments/operands of the object initializer expressions, recursively, if initializer is present.
## `default` expressions

View file

@ -13,7 +13,7 @@ This feature intends to allow developers to apply attributes directly to the bac
## Motivation
[motivation]: #motivation
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-targetting attribute they are forced to declare the field manually and use the more verbose property syntax. Given that C# has always supported field-targetted attributes on the generated backing field for events it makes sense to extend the same functionality to their property kin.
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.
## Detailed design
[design]: #detailed-design
@ -28,7 +28,7 @@ public class Foo {
}
```
This would result in the field-targetted attributes being applied to the compiler-generated backing field:
This would result in the field-targeted attributes being applied to the compiler-generated backing field:
```cs
[Serializable]
@ -59,7 +59,7 @@ public class Foo {
There are two potential drawbacks to implementing this change:
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.
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-targetted 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.
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.
## Alternatives
[alternatives]: #alternatives

View file

@ -0,0 +1,137 @@
# Unmanaged type constraint
* [x] Proposed
* [ ] Prototype
* [ ] Implementation
* [ ] Specification
## Summary
[summary]: #summary
The unmanaged constraint feature will give language enforcement to the class of types known as "unmanaged types" in the C# language spec. This is defined in section 18.2 as a type which is not a reference type and doesn't contain reference type fields at any level of nesting.
## Motivation
[motivation]: #motivation
The primary motivation is to make it easier to author low level interop code in C#. Unmanaged types are one of the core building blocks for interop code, yet the lack of support in generics makes it impossible to create re-usable routines across all unmanaged types. Instead developers are forced to author the same boiler plate code for every unmanaged type in their library:
``` c#
int Hash(Point point) { ... }
int Hash(TimeSpan timeSpan) { ... }
```
To enable this type of scenario the language will be introducing a new constraint: unmanaged:
``` c#
void Hash<T>(T value) where T : unmanaged
{
...
}
```
This constraint can only be met by types which fit into the unmanaged type definition in the C# language spec. Another way of looking at it is that a type satisfies the unmanaged constraint iff it can also be used as a pointer.
``` c#
Hash(new Point()); // Okay
Hash(42); // Okay
Hash("hello") // Error: Type string does not satisfy the unmanaged constraint
```
Type parameters with the unmanaged constraint can use all the features available to unmanaged types: pointers, fixed, etc ...
``` c#
void Hash<T>(T value) where T : unmanaged
{
// Okay
fixed (T* p = &value) {
...
}
}
```
This constraint will also make it possible to have efficient conversions between structured data and streams of bytes. This is an operation that is common in networking stacks and serialization layers:
``` c#
Span<byte> Convert<T>(ref T value) where T : unmanaged {
...
}
```
Such routines are advantageous because they are provably safe at compile time and allocation free. Interop authors today can not do this (even though it's at a layer where perf is critical). Instead they need to rely on allocating routines that have expensive runtime checks to verify values are correctly unmanaged.
## Detailed design
[design]: #detailed-design
The language will introduce a new constraint named `unmanaged`. In order to satisfy this constraint a type must be a struct and all the fields of the type must fall into one of the following categories:
- Have the type `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool`, `IntPtr` or `UIntPtr`.
- Be any `enum` type.
- Be a pointer type.
- Be a user defined struct that satsifies the `unmanaged` constraint.
Compiler generated instance fields, such as those backing auto-implemented properties, must also meet these constraints.
For example:
``` c#
// Unmanaged type
struct Point {
int X;
int Y {get; set;}
}
// Not an unmanaged type
struct Student {
string FirstName;
string LastName;
}
```
The `unmanaged` constraint cannot be combined with `struct`, `class` or `new()`. This restriction derives from the fact that `unmanaged` implies `struct` hence the other constraints do not make sense.
The `unmanaged` constraint is not enforced by CLR, only by the language. To prevent mis-use by other languages, methods which have this constraint will be protected by a mod-req. This will
prevent other languages from using type arguments which are not unmanaged types.
The token `unmanaged` in the constraint is not a keyword, nor a contextual keyword. Instead it is like `var` in that it is evaluated at that location and will either:
- Bind to user defined or referenced type named `unmanaged`: This will be treated just as any other named type constraint is treated.
- Bind to no type: This will be interpreted as the `unmanaged` constraint.
In the case there is a type named `unmanaged` and it is available without qualification in the current context, then there will be no way to use the `unmanaged` constraint. This parallels the rules surrounding the feature `var` and user defined types of the same name.
## Drawbacks
[drawbacks]: #drawbacks
The primary drawback of this feature is that it serves a small number of developers: typically low level library authors or frameworks. Hence it's spending precious language time for a small number of developers.
Yet these frameworks are often the basis for the majority of .NET applications out there. Hence performance / correctness wins at this level can have a ripple effect on the .NET ecosystem. This makes the feature worth considering even with the limited audience.
## Alternatives
[alternatives]: #alternatives
There are a couple of alternatives to consider:
- The status quo: The feature is not justified on its own merits and developers continue to use the implicit opt in behavior.
## Questions
[quesions]: #questions
### Metadata Representation
The F# language encodes the constraint in the signature file which means C# cannot re-use their representation. A new attribute will need to be chosen for this constraint. Additionally a method which has this constraint must be protected by a mod-req.
### Blittable vs. Unmanaged
The F# language has a very [similar feature](https://docs.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/generics/constraints) which uses the keyword unmanaged. The blittable name comes from the use in Midori. May want to look to precedence here and use unmanaged instead.
**Resolution** The language decide to use unmanaged
### Verifier
Does the verifier / runtime need to be updated to understand the use of pointers to generic type parameters? Or can it simply work as is without changes?
**Resolution** No changes needed. All pointer types are simply unverifiable.
## Design meetings
n/a

View file

@ -0,0 +1,15 @@
# Improved overload candidates
* [x] Proposed
* [x] Prototype
* [x] Implementation
* [ ] Specification
## Summary
[summary]: #summary
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.
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.
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.
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.

View file

@ -0,0 +1,52 @@
# Indexing `fixed` fields should not require pinning regardless of the movable/unmovable context. #
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.
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.
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.
```C#
unsafe struct S
{
public fixed int myFixedField[10];
}
class Program
{
static S s;
unsafe static void Main()
{
int p = s.myFixedField[5]; // indexing fixed-size array fields would be ok
}
}
```
The main “challenge” that I see here is how to explain the relaxation in the spec.
In particular, since the following would still need pinning.
(because `s` is moveable and we explicitly use the field as a pointer)
```C#
unsafe struct S
{
public fixed int myFixedField[10];
}
class Program
{
static S s;
unsafe static void Main()
{
int* ptr = s.myFixedField; // taking a pointer explicitly still requires pinning.
int p = ptr[5];
}
}
```
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 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 managed pointer. If we do that, then the intermediate ref is managed (GC-tracked) and the pinning is unnecessary.
The change https://github.com/dotnet/roslyn/pull/24966 is a prototype PR that relaxes this requirement.

View file

@ -0,0 +1,131 @@
# Pattern-based `fixed` statement
* [x] Proposed
* [ ] Prototype: Not Started
* [ ] Implementation: Not Started
* [ ] Specification: Not Started
## Summary
[summary]: #summary
Introduce a pattern that would allow types to participate in `fixed` statements.
## Motivation
[motivation]: #motivation
The language provides a mechanism for pinning managed data and obtain a native pointer to the underlying buffer.
```C#
fixed(byte* ptr = byteArray)
{
// ptr is a native pointer to the first element of the array
// byteArray is protected from being moved/collected by the GC for the duration of this block
}
```
The set of types that can participate in `fixed` is hardcoded and limited to arrays and `System.String`. Hardcoding "special" types does not scale when new primitives such as `ImmutableArray<T>`, `Span<T>`, `Utf8String` are introduced.
In addition, the current solution for `System.String` relies on a fairly rigid API. The shape of the API implies that `System.String` is a contiguous object that embeds UTF16 encoded data at a fixed offset from the object header. Such approach has been found problematic in several proposals that could require changes to the underlying layout.
It would be desirable to be able to switch to something more flexible that decouples `System.String` object from its internal representation for the purpose of unmanaged interop.
## Detailed design
[design]: #detailed-design
## *Pattern* ##
A viable pattern-based “fixed” need to:
- Provide the managed references to pin the instance and to initialize the pointer (preferably this is the same reference)
- Convey unambiguously the type of the unmanaged element (i.e. “char” for “string”)
- Prescribe the behavior in "empty" case when there is nothing to refer to.
- Should not push API authors toward design decisions that hurt the use of the type outside of `fixed`.
I think the above could be satisfied by recognizing a specially named ref-returning member:
`ref [readonly] T DangerousGetPinnableReference()`.
In order to be used by the `fixed` statement the following conditions must be met:
1) There is only one such member provided for a type.
1) Returns by `ref` or `ref readonly`.
(`readonly` is permitted so that authors of immutable/readonly types could implement the pattern without adding writeable API that could be used in safe code)
1) T is an unmanaged type.
(since `T*` becomes the pointer type. The restriction will naturally expand if/when the notion of "unmanaged" is expanded)
1) Returns managed `nullptr` when there is no data to pin probably the cheapest way to convey emptiness.
(note that “” string returns a ref to '\0' since strings are null-terminated)
Alternatively for the `#3` we can allow the result in empty cases be undefined or implementation-specific.
That, however, may make the API more dangerous and prone to abuse and unintended compatibility burdens.
## *Translation* ##
```C#
fixed(byte* ptr = thing)
{
// <BODY>
}
```
becomes the following pseudocode (not all expressible in C#)
```C#
byte* ptr;
// specially decorated "pinned" IL local slot, not visible to user code.
pinned ref byte _pinned;
try
{
// NOTE: null check is omitted for value types
// NOTE: `thing` is evaluated only once (temporary is introduced if necessary)
if (thing != null)
{
// obtain and "pin" the reference
_pinned = ref thing.DangerousGetPinnableReference();
// unsafe cast in IL
ptr = (byte*)_pinned;
}
else
{
ptr = default(byte*);
}
// <BODY>
}
finally // finally can be omitted when not observable
{
// "unpin" the object
_pinned = nullptr;
}
```
## Drawbacks
[drawbacks]: #drawbacks
- DangerousGetPinnableReference is intended to be used only in `fixed`, but nothing prevents its use in safe code, so implementor must keep that in mind.
## Alternatives
[alternatives]: #alternatives
Users can introduce DangerousGetPinnableReference or similar member and use it as
```C#
fixed(byte* ptr = thing.DangerousGetPinnableReference())
{
// <BODY>
}
```
There is no solution for `System.String` if alternative solution is desired.
## Unresolved questions
[unresolved]: #unresolved-questions
- [ ] Behavior in "empty" state. - `nullptr` or `undefined` ?
- [ ] Should the extension methods be considered ?
- [ ] If a pattern is detected on `System.String`, should it win over ?
## Design meetings
None yet.

View file

@ -0,0 +1,65 @@
# Stackalloc array initializers.
* [x] Proposed
* [ ] Prototype: Not Started
* [ ] Implementation: Not Started
* [ ] Specification: Not Started
## Summary
[summary]: #summary
Allow array initializer syntax to be used with `stackalloc`
## Motivation
[motivation]: #motivation
Ordinary arrays can have their elements initialized at creation time. It seems reasonable to allow that in `stackalloc` case.
The question of why such syntax is not allowed with `stackalloc` arises fairly frequently.
See, for example, https://github.com/dotnet/csharplang/issues/1112
## Detailed design
Ordinary arrays can be created through the following syntax:
```C#
new int[3]
new int[3] { 1, 2, 3 }
new int[] { 1, 2, 3 }
new[] { 1, 2, 3 }
```
We should allow stack allocated arrays be created through:
```C#
stackalloc int[3] // currently allowed
stackalloc int[3] { 1, 2, 3 }
stackalloc int[] { 1, 2, 3 }
stackalloc[] { 1, 2, 3 }
```
The semantics of all cases is roughly the same as with arrays.
For example: in the last case the element type is inferred from the initializer and must be an "unmanaged" type.
NOTE: the feature is not dependent on the target being a `Span<T>`. It is just as applicable in `T*` case, so it does not seem reasonable to predicate it on `Span<T>` case.
## Translation ##
The naive implementation could just initialize the array right after creation through a series of element-wise assignments.
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.
## Drawbacks
[drawbacks]: #drawbacks
## Alternatives
[alternatives]: #alternatives
This is a convenience feature. It is possible to just do nothing.
## Unresolved questions
[unresolved]: #unresolved-questions
## Design meetings
None yet.

View file

@ -0,0 +1,77 @@
# Support for == and != on tuple types
Allow expressions `t1 == t2` where `t1` and `t2` are tuple or nullable tuple types of same cardinality, and evaluate them roughly as `temp1.Item1 == temp2.Item1 && temp1.Item2 == temp2.Item2` (assuming `var temp1 = t1; var temp2 = t2;`).
Conversely it would allow `t1 != t2` and evaluate it as `temp1.Item1 != temp2.Item1 || temp1.Item2 != temp2.Item2`.
In the nullable case, additional checks for `temp1.HasValue` and `temp2.HasValue` are used. For instance, `nullableT1 == nullableT2` evaluates as `temp1.HasValue == temp2.HasValue ? (temp1.HasValue ? ... : true) : false`.
When an element-wise comparison returns a non-bool result (for instance, when a non-bool user-defined `operator ==` or `operator !=` is used, or in a dynamic comparison), then that result will be either converted to `bool` or run through `operator true` or `operator false` to get a `bool`. The tuple comparison always ends up returning a `bool`.
As of C# 7.2, such code produces an error (`error CS0019: Operator '==' cannot be applied to operands of type '(...)' and '(...)'`), unless there is a user-defined `operator==`.
## Details
When binding the `==` (or `!=`) operator, the existing rules are: (1) dynamic case, (2) overload resolution, and (3) fail.
This proposal adds a tuple case between (1) and (2): if both operands of a comparison operator are tuples (have tuple types or are tuple literals) and have matching cardinality, then the comparison is performed element-wise. This tuple equality is also lifted onto nullable tuples.
Both operands (and, in the case of tuple literals, their elements) are evaluated in order from left to right. Each pair of elements is then used as operands to bind the operator `==` (or `!=`), recursively. Any elements with compile-time type `dynamic` cause an error. The results of those element-wise comparisons are used as operands in a chain of conditional AND (or OR) operators.
For instance, in the context of `(int, (int, int)) t1, t2;`, `t1 == (1, (2, 3))` would evaluate as `temp1.Item1 == temp2.Item1 && temp1.Item2.Item1 == temp2.Item2.Item1 && temp2.Item2.Item2 == temp2.Item2.Item2`.
When a tuple literal is used as operand (on either side), it receives a converted tuple type formed by the element-wise conversions which are introduced when binding the operator `==` (or `!=`) element-wise.
For instance, in `(1L, 2, "hello") == (1, 2L, null)`, the converted type for both tuple literals is `(long, long, string)` and the second literal has no natural type.
### Deconstruction and conversions to tuple
In `(a, b) == x`, the fact that `x` can deconstruct into two elements does not play a role. That could conceivably be in a future proposal, although it would raise questions about `x == y` (is this a simple comparison or an element-wise comparison, and if so using what cardinality?).
Similarly, conversions to tuple play no role.
### Tuple element names
When converting a tuple literal, we warn when an explicit tuple element name was provided in the literal, but it doesn't match the target tuple element name.
We use the same rule in tuple comparison, so that assuming `(int a, int b) t` we warn on `d` in `t == (c, d: 0)`.
### Non-bool element-wise comparison results
If an element-wise comparison is dynamic in a tuple equality, we use a dynamic invocation of the operator `false` and negate that to get a `bool` and continue with further element-wise comparisons.
If an element-wise comparison returns some other non-bool type in a tuple equality, there are two cases:
- if the non-bool type converts to `bool`, we apply that conversion,
- if there is no such conversion, but the type has an operator `false`, we'll use that and negate the result.
In a tuple inequality, the same rules apply except that we'll use the operator `true` (without negation) instead of the operator `true`.
Those rules are similar the rules involved for using a non-bool type in an `if` statement and some other existing contexts.
## Evaluation order and special cases
The left-hand-side value is evaluated first, then the right-hand-side value, then the element-wise comparisons from left to right (including conversions, and with early exit based on existing rules for conditional AND/OR operators).
For instance, if there is a conversion from type `A` to type `B` and a method `(A, A) GetTuple()`, evaluating `(new A(1), (new B(2), new B(3))) == (new B(4), GetTuple())` means:
- `new A(1)`
- `new B(2)`
- `new B(3)`
- `new B(4)`
- `GetTuple()`
- then the element-wise conversions and comparisons and conditional logic is evaluated (convert `new A(1)` to type `B`, then compare it with `new B(4)`, and so on).
### Comparing `null` to `null`
This is a special case from regular comparisons, that carries over to tuple comparisons. The `null == null` comparison is allowed, and the `null` literals do not get any type.
In tuple equality, this means, `(0, null) == (0, null)` is also allowed and the `null` and tuple literals don't get a type either.
### Comparing a nullable struct to `null` without `operator==`
This is another special case from regular comparisons, that carries over to tuple comparisons.
If you have a `struct S` without `operator==`, the `(S?)x == null` comparison is allowed, and it is interpreted as `((S?).x).HasValue`.
In tuple equality, the same rule is applied, so `(0, (S?)x) == (0, null)` is allowed.
## Compatibility
If someone wrote their own `ValueTuple` types with an implementation of the comparison operator, it would have previously been picked up by overload resolution. But since the new tuple case comes before overload resolution, we would handle this case with tuple comparison instead of relying on the user-defined comparison.
----
Relates to https://github.com/dotnet/csharplang/blob/master/spec/expressions.md#relational-and-type-testing-operators
Relates to https://github.com/dotnet/csharplang/issues/190

View file

@ -0,0 +1,73 @@
# Declaration expressions
Support declaration assignments as expressions.
## Motivation
[motivation]: #motivation
Allow initialization at the point of declaration in more cases, simplifying code, and allowing `var` to be used.
```C#
SpecialType ReferenceType =>
(var st = _type.SpecialType).IsValueType() ? SpecialType.None : st;
```
Allow declarations for `ref` arguments, similar to `out var`.
```C#
Convert(source, destination, ref List<Diagnostic> diagnostics = null);
```
## Detailed design
[design]: #detailed-design
Expressions are extended to include declaration assignment. Precedence is the same as assignment.
```antlr
expression
: non_assignment_expression
| assignment
| declaration_assignment_expression // new
;
declaration_assignment_expression // new
: declaration_expression '=' local_variable_initializer
;
declaration_expression // C# 7.0
| type variable_designation
;
```
The declaration assignment is of a single local.
The type of a declaration assignment expression is the type of the declaration.
If the type is `var`, the inferred type is the type of the initializing expression.
The declaration assignment expression may be an l-value, for `ref` argument values in particular.
If the declaration assignment expression declares a value type, and the expression is an r-value, the value of
the expression is a copy.
The declaration assignment expression may declare a `ref` local.
There is an ambiguity when `ref` is used for a declaration expression in a `ref` argument.
The local variable initializer determines whether the declaration is a `ref` local.
```C#
F(ref int x = IntFunc()); // int x;
F(ref int y = RefIntFunc()); // ref int y;
```
The scope of locals declared in declaration assignment expressions is the same the scope of corresponding declaration expressions from C#7.0.
It is a compile time error to refer to a local in text preceding the declaration expression.
## Alternatives
[alternatives]: #alternatives
No change. This feature is just syntactic shorthand after all.
More general sequence expressions: see [#377](https://github.com/dotnet/csharplang/issues/377).
To allow use of `var` in more cases, allow separate declaration and assignment of `var` locals,
and infer the type from assignments from all code paths.
## See also
[see-also]: #see-also
See Basic Declaration Expression in [#595](https://github.com/dotnet/csharplang/issues/595).
See Deconstruction Declaration in the [deconstruction](https://github.com/dotnet/roslyn/blob/master/docs/features/deconstruction.md) feature.

View file

@ -138,7 +138,7 @@ interface IB : IA
Override declarations in interfaces may not be declared `sealed`.
Public `virtual` function members in an interface may be overriden in a derived interface either implicitly (by using the `override` modifier in a public declaration in the derived interface) or explicitly (by qualifying the name in the override declaration with the interface type that originally declared the method, and omitting an access modifier).
Public `virtual` function members in an interface may be overridden in a derived interface either implicitly (by using the `override` modifier in a public declaration in the derived interface) or explicitly (by qualifying the name in the override declaration with the interface type that originally declared the method, and omitting an access modifier).
`virtual` function members in an interface that are not `public` may only be overridden explicitly (not implicitly) in derived interfaces, and may only be implemented in a class or struct explicitly (not implicitly). In either case, the overridden or implemented member must be *accessible* where it is overridden.

View file

@ -15,6 +15,8 @@ Provide a general-purpose and safe mechanism for declaring fixed sized buffers t
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`).
The most common complaint is that fixed-size buffers cannot be indexed in safe code. Inability to use more types is the second.
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.
## Detailed design
@ -29,42 +31,51 @@ public fixed DXGI_RGB GammaCurve[1025];
The declaration would get translated into an internal representation by the compiler that is similar to the following
```C#
[CompilerGenerated, StructLayout(LayoutKind.Sequential, Pack = 1)]
struct <GammaCurve>e__FixedBuffer
{
private DXGI_RGB _e0;
private DXGI_RGB _e1;
// _e2 ... _e1023
private DXGI_RGB _e1024;
public ref DXGI_RGB this[int index]
{
get;
}
[FixedBuffer(typeof(DXGI_RGB), 1024)]
public ConsoleApp1.<Buffer>e__FixedBuffer_1024<DXGI_RGB> GammaCurve;
// Pack = 0 is the default packing and should result in indexable layout.
[CompilerGenerated, UnsafeValueType, StructLayout(LayoutKind.Sequential, Pack = 0)]
struct <Buffer>e__FixedBuffer_1024<T>
{
private T _e0;
private T _e1;
// _e2 ... _e1023
private T _e1024;
public ref T this[int index] => ref (uint)index <= 1024u ?
ref RefAdd<T>(ref _e0, index):
throw new IndexOutOfRange();
}
```
Since such fixed-sized buffers no longer require use of `fixed`, it makes sense to allow any element type.
> NOTE: `fixed` will still be supported, but only if the element type is `blittable`
## Drawbacks
[drawbacks]: #drawbacks
Some additional syntax may be needed to ensure we don't break users that are currently using 'unsafe fixed-buffers'.
* 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.
* Its probably worth extending the ability to work with fixed-sized buffers as pointers to all `blittable` types.
In order to ensure that the safe fixed-sized buffer is resilient to future field-layout changes (and for it to work with non-blittable types), the compiler needs to explicitly layout each field of the array.
* This can quickly become 'unwieldly' for large arrays
* The JIT may not behave well with types that contain a large number of fields
* We may be able to work with the JIT/Runtime to provide a special flag they can use (basically, it would give the contained type and the element count, then they just need to compute the size of the base struct and multiply, rather than actually checking each field).
* 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.
* Incompatible constructs may need to use slightly different `v2` encoding to hide the fields from old compiler.
* 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.
* 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.
* `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.
## Alternatives
[alternatives]: #alternatives
Manually declare your structures
Manually declare your structures and use unsafe code to construct indexers.
## Unresolved questions
[unresolved]: #unresolved-questions
Can/should this additionally be extended to stackalloc which provides a very similar mechanism.
- should we allow `readonly`? (with readonly indexer)
- should we allow array initializers?
- is `fixed` keyword necessary?
- `foreach`?
- only instance fields in structs?
## Design meetings

View file

@ -0,0 +1,91 @@
# Suppress emitting of `localsinit` flag.
* [x] Proposed
* [ ] Prototype: Not Started
* [ ] Implementation: Not Started
* [ ] Specification: Not Started
## Summary
[summary]: #summary
Allow suppressing emit of `localsinit` flag via `SkipLocalsInitAttribute` attribute.
## Motivation
[motivation]: #motivation
### Background
Per CLR spec local variables that do not contain references are not initialized to a particular value by the VM/JIT. Reading from such variables without initialization is type-safe, but otherwise the behavior is undefined and implementation specific. Typically uninitialized locals contain whatever values were left in the memory that is now occupied by the stack frame. That could lead to nondeterministic behavior and hard to reproduce bugs.
There are two ways to "assign" a local variable:
- by storing a value or
- by specifying `localsinit` flag which forces everything that is allocated form the local memory pool to be zero-initialized
NOTE: this includes both local variables and `stackalloc` data.
Use of uninitialized data is discouraged and is not allowed in verifiable code. While it might be possible to prove that by the means of flow analysis, it is permitted for the verification algorithm to be conservative and simply require that `localsinit` is set.
Historically C# compiler emits `localsinit` flag on all methods that declare locals.
While C# employs definite-assignment analysis which is more strict than what CLR spec would require (C# also needs to consider scoping of locals), it is not strictly guaranteed that the resulting code would be formally verifiable:
- CLR and C# rules may not agree on whether passing a local as `out` argument is a `use`.
- CLR and C# rules may not agree on treatment of conditional branches when conditions are known (constant propagation).
- CLR could as well simply require `localinits`, since that is permitted.
### Problem
In high-performance application the cost of forced zero-initialization could be noticeable. It is particularly noticeable when `stackalloc` is used.
In some cases JIT can elide initial zero-initialization of individual locals when such initialization is "killed" by subsequent assignments. Not all JITs do this and such optimization has limits. It does not help with `stackalloc`.
To illustrate that the problem is real - there is a known bug where a method not containing any `IL` locals would not have `localsinit` flag. The bug is already being exploited by users by putting `stackalloc` into such methods - intentionally to avoid initialization costs. That is despite the fact that absence of `IL` locals is an unstable metric and may vary depending on changes in codegen strategy.
The bug should be fixed and users should get a more documented and reliable way of suppressing the flag.
## Detailed design
Allow specifying `System.Runtime.CompilerServices.SkipLocalsInitAttribute` as a way to tell the compiler to not emit `localsinit` flag.
The end result of this will be that the locals may not be zero-initialized by the JIT, which is in most cases unobservable in C#.
In addition to that `stackalloc` data will not be zero-initialized. That is definitely observable, but also is the most motivating scenario.
Permitted and recognized attribute targets are: `Method`, `Property`, `Module`, `Class`, `Struct`, `Interface`, `Constructor`. However compiler will not require that attribute is defined with the listed targets nor it will care in which assembly the attribute is defined.
When attribute is specified on a container (`class`, `module`, containing method for a nested method, ...), the flag affects all methods contained within the container.
Synthesized methods "inherit" the flag from the logical container/owner.
The flag affects only codegen strategy for actual method bodies. I.E. the flag has no effect on abstract methods and is not propagated to overriding/implementing methods.
This is explicitly a **_compiler feature_** and **_not a language feature_**.
Similarly to compiler command line switches the feature controls implementation details of a particular codegen strategy and does not need to be required by the C# spec.
## Drawbacks
[drawbacks]: #drawbacks
- Old/other compilers may not honor the attribute.
Ignoring the attribute is compatible behavior. Only may result in a slight perf hit.
- The code without `localinits` flag may trigger verification failures.
Users that ask for this feature are generally unconcerned with verifiability.
- Applying the attribute at higher levels than an individual method has nonlocal effect, which is observable when `stackalloc` is used.
Yet, this is the most requested scenario.
## Alternatives
[alternatives]: #alternatives
- omit `localinits` flag when method is declared in `unsafe` context.
That could cause silent and dangerous behavior change from deterministic to nonditerministic in a case of `stackalloc` .
- omit `localinits` flag always.
Even worse than above.
- omit `localinits` flag unless `stackalloc` is used in the method body.
Does not address the most requested scenario and may turn code unverifiable with no option to revert that back.
## Unresolved questions
[unresolved]: #unresolved-questions
- Should the attribute be actually emitted to metadata?
## Design meetings
None yet.

View file

@ -0,0 +1,99 @@
# Target-typed `new` expressions
* [x] Proposed
* [x] [Prototype](https://github.com/alrz/roslyn/tree/features/target-typed-new)
* [ ] Implementation
* [ ] Specification
## Summary
[summary]: #summary
Do not require type specification for constructors when the type is known.
## Motivation
[motivation]: #motivation
Allow field initialization without duplicating the type.
```cs
Dictionary<string, List<int>> field = new() {
{ "item1", new() { 1, 2, 3 } }
};
```
Allow omitting the type when it can be inferred from usage.
```cs
XmlReader.Create(reader, new() { IgnoreWhitespace = true });
```
## Detailed design
[design]: #detailed-design
The *object_creation_expression* syntax would be modified to make the *type* optional when parentheses are present. This is required to address the ambiguity with *anonymous_object_creation_expression*.
```antlr
object_creation_expression
: 'new' type? '(' argument_list? ')' object_or_collection_initializer?
| 'new' type object_or_collection_initializer
;
```
The target-type is resolved through a conversion that only exists if and only if there exists a single best accessible constructor with regard to provided arguments. Target of the initialization is determined after the conversion succeeded.
The type of the expression would be inferred from the target-type which would be required to be one of the following:
- **Any struct type**
- **Any reference type**
- **Any type parameter** with a constructor or a `struct` constraint
with the following exceptions:
- **Enum types:** not all enum types contain the constant zero, so it should be desirable to use the explicit enum member.
- **Delegate types:** if the type is inferrable, an anonymous function can already be used.
- **Interface types:** this is a niche feature and it should be preferable to explicitly mention the type.
- **Tuple types:** tuple literals should be used for such expressions.
- **Array types:** arrays need a special syntax to provide the length.
All the other types that are not permitted in the *object_creation_expression* are excluded as well, for instance, pointer types.
The default constructor for a value type (unless appeared explicitly in metadata) is excluded, which has the effect of excluding the primitive types and the default constructor for most value types. If you wanted to use the default value of such types you could write `default`.
> **Open Issue:** What other types should be excluded?
Note that any restriction on permitted types would raise the success rate of the overload resolution. For example, the following would successfully compile considering the restriction on the default constructor for value types.
```cs
class C {}
void M(C c) {}
void M(int i) {}
M(new());
```
Otherwise it would fail with an ambiguous call error.
## Drawbacks
[drawbacks]: #drawbacks
Since we're relying on overload resolution of the target-type members, there is a high chance that any change in one of the participating members (target-type's constructors or call-site method overloads, if any) would result in breaking the compilation. For instance,
```cs
class Foo { }
class Bar { }
void M(Foo foo) {}
M(new());
```
The invocation compiles until an overload like `void M(Bar bar) {}` is added alongside of the existing method.
## Alternatives
[alternatives]: #alternatives
Most of complaints about types being too long to duplicate in field initialization is about *type arguments* not the type itself, we could infer only type arguments like `new Dictionary(...)` (or similar) instead of the whole type, in which case, it would be a lot less likely to break the compilation with adding a constructor or an overloaded member, because we're not relying on the target-type, rather, we infer type arguments locally from arguments or the collection initializer.
## Questions
[quesions]: #questions
- Should we forbid usages in expression trees?
- How the feature interacts with `dynamic` arguments?
- It's not clear how IntelliSense should behave when there are multiple target-types, specially in the nested case `M(new(new()));`.
## Design meetings
- [LDM-2017-10-18](https://github.com/dotnet/csharplang/blob/master/meetings/2017/LDM-2017-10-18.md#100)

View file

@ -44,7 +44,7 @@ Prior to an application's termination, destructors for all of its objects that h
## Declarations
Declarations in a C# program define the constituent elements of the program. C# programs are organized using namespaces ([Namespaces](namespaces.md#namespaces)), which can contain type declarations and nested namespace declarations. Type declarations ([Type declarations](namespaces.md#type-declarations)) are used to define classes ([Classes](classes.md#classes)), structs ([Iterators](classes.md#iterators)), interfaces ([Interfaces](interfaces.md#interfaces)), enums ([Enums](enums.md#enums)), and delegates ([Delegates](delegates.md#delegates)). The kinds of members permitted in a type declaration depend on the form of the type declaration. For instance, class declarations can contain declarations for constants ([Constants](classes.md#constants)), fields ([Fields](classes.md#fields)), methods ([Methods](classes.md#methods)), properties ([Properties](classes.md#properties)), events ([Events](classes.md#events)), indexers ([Indexers](classes.md#indexers)), operators ([Operators](classes.md#operators)), instance constructors ([Instance constructors](classes.md#instance-constructors)), static constructors ([Static constructors](classes.md#static-constructors)), destructors ([Destructors](classes.md#destructors)), and nested types ([Nested types](classes.md#nested-types)).
Declarations in a C# program define the constituent elements of the program. C# programs are organized using namespaces ([Namespaces](namespaces.md)), which can contain type declarations and nested namespace declarations. Type declarations ([Type declarations](namespaces.md#type-declarations)) are used to define classes ([Classes](classes.md)), structs ([Structs](structs.md)), interfaces ([Interfaces](interfaces.md)), enums ([Enums](enums.md)), and delegates ([Delegates](delegates.md)). The kinds of members permitted in a type declaration depend on the form of the type declaration. For instance, class declarations can contain declarations for constants ([Constants](classes.md#constants)), fields ([Fields](classes.md#fields)), methods ([Methods](classes.md#methods)), properties ([Properties](classes.md#properties)), events ([Events](classes.md#events)), indexers ([Indexers](classes.md#indexers)), operators ([Operators](classes.md#operators)), instance constructors ([Instance constructors](classes.md#instance-constructors)), static constructors ([Static constructors](classes.md#static-constructors)), destructors ([Destructors](classes.md#destructors)), and nested types ([Nested types](classes.md#nested-types)).
A declaration defines a name in the ***declaration space*** to which the declaration belongs. Except for overloaded members ([Signatures and overloading](basic-concepts.md#signatures-and-overloading)), it is a compile-time error to have two or more declarations that introduce members with the same name in a declaration space. It is never possible for a declaration space to contain different kinds of members with the same name. For example, a declaration space can never contain a field and a method by the same name.
@ -420,7 +420,7 @@ Signatures are the enabling mechanism for ***overloading*** of members in classe
Although `out` and `ref` parameter modifiers are considered part of a signature, members declared in a single type cannot differ in signature solely by `ref` and `out`. A compile-time error occurs if two members are declared in the same type with signatures that would be the same if all parameters in both methods with `out` modifiers were changed to `ref` modifiers. For other purposes of signature matching (e.g., hiding or overriding), `ref` and `out` are considered part of the signature and do not match each other. (This restriction is to allow C#  programs to be easily translated to run on the Common Language Infrastructure (CLI), which does not provide a way to define methods that differ solely in `ref` and `out`.)
For the purposes of singatures, the types `object` and `dynamic` are considered the same. Members declared in a single type can therefore not differ in signature solely by `object` and `dynamic`.
For the purposes of signatures, the types `object` and `dynamic` are considered the same. Members declared in a single type can therefore not differ in signature solely by `object` and `dynamic`.
The following example shows a set of overloaded method declarations along with their signatures.
```csharp

View file

@ -13,7 +13,7 @@ class_declaration
;
```
A *class_declaration* consists of an optional set of *attributes* ([Attributes](attributes.md#attributes)), followed by an optional set of *class_modifier*s ([Class modifiers](classes.md#class-modifiers)), followed by an optional `partial` modifier, followed by the keyword `class` and an *identifier* that names the class, followed by an optional *type_parameter_list* ([Type parameters](classes.md#type-parameters)), followed by an optional *class_base* specification ([Class base specification](classes.md#class-base-specification)) , followed by an optional set of *type_parameter_constraints_clause*s ([Type parameter constraints](classes.md#type-parameter-constraints)), followed by a *class_body* ([Class body](classes.md#class-body)), optionally followed by a semicolon.
A *class_declaration* consists of an optional set of *attributes* ([Attributes](attributes.md)), followed by an optional set of *class_modifier*s ([Class modifiers](classes.md#class-modifiers)), followed by an optional `partial` modifier, followed by the keyword `class` and an *identifier* that names the class, followed by an optional *type_parameter_list* ([Type parameters](classes.md#type-parameters)), followed by an optional *class_base* specification ([Class base specification](classes.md#class-base-specification)) , followed by an optional set of *type_parameter_constraints_clause*s ([Type parameter constraints](classes.md#type-parameter-constraints)), followed by a *class_body* ([Class body](classes.md#class-body)), optionally followed by a semicolon.
A class declaration cannot supply *type_parameter_constraints_clause*s unless it also supplies a *type_parameter_list*.
@ -120,7 +120,7 @@ Having the declaration of a class distributed over separate segments of program
### Type parameters
A type parameter is a simple identifier that denotes a placeholder for a type argument supplied to create a constructed type. A type parameter is a formal placeholder for a type that will be supplied later. By constrast, a type argument ([Type arguments](types.md#type-arguments)) is the actual type that is substituted for the type parameter when a constructed type is created.
A type parameter is a simple identifier that denotes a placeholder for a type argument supplied to create a constructed type. A type parameter is a formal placeholder for a type that will be supplied later. By contrast, a type argument ([Type arguments](types.md#type-arguments)) is the actual type that is substituted for the type parameter when a constructed type is created.
```antlr
type_parameter_list
@ -141,7 +141,7 @@ Each type parameter in a class declaration defines a name in the declaration spa
### Class base specification
A class declaration may include a *class_base* specification, which defines the direct base class of the class and the interfaces ([Interfaces](interfaces.md#interfaces)) directly implemented by the class.
A class declaration may include a *class_base* specification, which defines the direct base class of the class and the interfaces ([Interfaces](interfaces.md)) directly implemented by the class.
```antlr
class_base
@ -1271,7 +1271,7 @@ constant_declarator
;
```
A *constant_declaration* may include a set of *attributes* ([Attributes](attributes.md#attributes)), a `new` modifier ([The new modifier](classes.md#the-new-modifier)), and a valid combination of the four access modifiers ([Access modifiers](classes.md#access-modifiers)). The attributes and modifiers apply to all of the members declared by the *constant_declaration*. Even though constants are considered static members, a *constant_declaration* neither requires nor allows a `static` modifier. It is an error for the same modifier to appear multiple times in a constant declaration.
A *constant_declaration* may include a set of *attributes* ([Attributes](attributes.md)), a `new` modifier ([The new modifier](classes.md#the-new-modifier)), and a valid combination of the four access modifiers ([Access modifiers](classes.md#access-modifiers)). The attributes and modifiers apply to all of the members declared by the *constant_declaration*. Even though constants are considered static members, a *constant_declaration* neither requires nor allows a `static` modifier. It is an error for the same modifier to appear multiple times in a constant declaration.
The *type* of a *constant_declaration* specifies the type of the members introduced by the declaration. The type is followed by a list of *constant_declarator*s, each of which introduces a new member. A *constant_declarator* consists of an *identifier* that names the member, followed by an "`=`" token, followed by a *constant_expression* ([Constant expressions](expressions.md#constant-expressions)) that gives the value of the member.
@ -1354,7 +1354,7 @@ variable_initializer
;
```
A *field_declaration* may include a set of *attributes* ([Attributes](attributes.md#attributes)), a `new` modifier ([The new modifier](classes.md#the-new-modifier)), a valid combination of the four access modifiers ([Access modifiers](classes.md#access-modifiers)), and a `static` modifier ([Static and instance fields](classes.md#static-and-instance-fields)). In addition, a *field_declaration* may include a `readonly` modifier ([Readonly fields](classes.md#readonly-fields)) or a `volatile` modifier ([Volatile fields](classes.md#volatile-fields)) but not both. The attributes and modifiers apply to all of the members declared by the *field_declaration*. It is an error for the same modifier to appear multiple times in a field declaration.
A *field_declaration* may include a set of *attributes* ([Attributes](attributes.md)), a `new` modifier ([The new modifier](classes.md#the-new-modifier)), a valid combination of the four access modifiers ([Access modifiers](classes.md#access-modifiers)), and a `static` modifier ([Static and instance fields](classes.md#static-and-instance-fields)). In addition, a *field_declaration* may include a `readonly` modifier ([Readonly fields](classes.md#readonly-fields)) or a `volatile` modifier ([Volatile fields](classes.md#volatile-fields)) but not both. The attributes and modifiers apply to all of the members declared by the *field_declaration*. It is an error for the same modifier to appear multiple times in a field declaration.
The *type* of a *field_declaration* specifies the type of the members introduced by the declaration. The type is followed by a list of *variable_declarator*s, each of which introduces a new member. A *variable_declarator* consists of an *identifier* that names that member, optionally followed by an "`=`" token and a *variable_initializer* ([Variable initializers](classes.md#variable-initializers)) that gives the initial value of that member.
@ -1381,7 +1381,7 @@ class A
### Static and instance fields
When a field declaration includes a `static` modifier, the fields introduced by the declaration are ***static fields***. When no `static` modifier is present, the fields introduced by the declaration are ***instance fields***. Static fields and instance fields are two of the several kinds of variables ([Variables](variables.md#variables)) supported by C#, and at times they are referred to as ***static variables*** and ***instance variables***, respectively.
When a field declaration includes a `static` modifier, the fields introduced by the declaration are ***static fields***. When no `static` modifier is present, the fields introduced by the declaration are ***instance fields***. Static fields and instance fields are two of the several kinds of variables ([Variables](variables.md)) supported by C#, and at times they are referred to as ***static variables*** and ***instance variables***, respectively.
A static field is not part of a specific instance; instead, it is shared amongst all instances of a closed type ([Open and closed types](types.md#open-and-closed-types)). No matter how many instances of a closed class type are created, there is only ever one copy of a static field for the associated application domain.
@ -1744,7 +1744,7 @@ method_body
;
```
A *method_declaration* may include a set of *attributes* ([Attributes](attributes.md#attributes)) and a valid combination of the four access modifiers ([Access modifiers](classes.md#access-modifiers)), the `new` ([The new modifier](classes.md#the-new-modifier)), `static` ([Static and instance methods](classes.md#static-and-instance-methods)), `virtual` ([Virtual methods](classes.md#virtual-methods)), `override` ([Override methods](classes.md#override-methods)), `sealed` ([Sealed methods](classes.md#sealed-methods)), `abstract` ([Abstract methods](classes.md#abstract-methods)), and `extern` ([External methods](classes.md#external-methods)) modifiers.
A *method_declaration* may include a set of *attributes* ([Attributes](attributes.md)) and a valid combination of the four access modifiers ([Access modifiers](classes.md#access-modifiers)), the `new` ([The new modifier](classes.md#the-new-modifier)), `static` ([Static and instance methods](classes.md#static-and-instance-methods)), `virtual` ([Virtual methods](classes.md#virtual-methods)), `override` ([Override methods](classes.md#override-methods)), `sealed` ([Sealed methods](classes.md#sealed-methods)), `abstract` ([Abstract methods](classes.md#abstract-methods)), and `extern` ([External methods](classes.md#external-methods)) modifiers.
A declaration has a valid combination of modifiers if all of the following are true:
@ -1821,7 +1821,7 @@ parameter_array
The formal parameter list consists of one or more comma-separated parameters of which only the last may be a *parameter_array*.
A *fixed_parameter* consists of an optional set of *attributes* ([Attributes](attributes.md#attributes)), an optional `ref`, `out` or `this` modifier, a *type*, an *identifier* and an optional *default_argument*. Each *fixed_parameter* declares a parameter of the given type with the given name. The `this` modifier designates the method as an extension method and is only allowed on the first parameter of a static method. Extension methods are further described in [Extension methods](classes.md#extension-methods).
A *fixed_parameter* consists of an optional set of *attributes* ([Attributes](attributes.md)), an optional `ref`, `out` or `this` modifier, a *type*, an *identifier* and an optional *default_argument*. Each *fixed_parameter* declares a parameter of the given type with the given name. The `this` modifier designates the method as an extension method and is only allowed on the first parameter of a static method. Extension methods are further described in [Extension methods](classes.md#extension-methods).
A *fixed_parameter* with a *default_argument* is known as an ***optional parameter***, whereas a *fixed_parameter* without a *default_argument* is a ***required parameter***. A required parameter may not appear after an optional parameter in a *formal_parameter_list*.
@ -1835,7 +1835,7 @@ The *expression* must be implicitly convertible by an identity or nullable conve
If optional parameters occur in an implementing partial method declaration ([Partial methods](classes.md#partial-methods)) , an explicit interface member implementation ([Explicit interface member implementations](interfaces.md#explicit-interface-member-implementations)) or in a single-parameter indexer declaration ([Indexers](classes.md#indexers)) the compiler should give a warning, since these members can never be invoked in a way that permits arguments to be omitted.
A *parameter_array* consists of an optional set of *attributes* ([Attributes](attributes.md#attributes)), a `params` modifier, an *array_type*, and an *identifier*. A parameter array declares a single parameter of the given array type with the given name. The *array_type* of a parameter array must be a single-dimensional array type ([Array types](arrays.md#array-types)). In a method invocation, a parameter array permits either a single argument of the given array type to be specified, or it permits zero or more arguments of the array element type to be specified. Parameter arrays are described further in [Parameter arrays](classes.md#parameter-arrays).
A *parameter_array* consists of an optional set of *attributes* ([Attributes](attributes.md)), a `params` modifier, an *array_type*, and an *identifier*. A parameter array declares a single parameter of the given array type with the given name. The *array_type* of a parameter array must be a single-dimensional array type ([Array types](arrays.md#array-types)). In a method invocation, a parameter array permits either a single argument of the given array type to be specified, or it permits zero or more arguments of the array element type to be specified. Parameter arrays are described further in [Parameter arrays](classes.md#parameter-arrays).
A *parameter_array* may occur after an optional parameter, but cannot have a default value -- the omission of arguments for a *parameter_array* would instead result in the creation of an empty array.
@ -2584,7 +2584,7 @@ property_initializer
;
```
A *property_declaration* may include a set of *attributes* ([Attributes](attributes.md#attributes)) and a valid combination of the four access modifiers ([Access modifiers](classes.md#access-modifiers)), the `new` ([The new modifier](classes.md#the-new-modifier)), `static` ([Static and instance methods](classes.md#static-and-instance-methods)), `virtual` ([Virtual methods](classes.md#virtual-methods)), `override` ([Override methods](classes.md#override-methods)), `sealed` ([Sealed methods](classes.md#sealed-methods)), `abstract` ([Abstract methods](classes.md#abstract-methods)), and `extern` ([External methods](classes.md#external-methods)) modifiers.
A *property_declaration* may include a set of *attributes* ([Attributes](attributes.md)) and a valid combination of the four access modifiers ([Access modifiers](classes.md#access-modifiers)), the `new` ([The new modifier](classes.md#the-new-modifier)), `static` ([Static and instance methods](classes.md#static-and-instance-methods)), `virtual` ([Virtual methods](classes.md#virtual-methods)), `override` ([Override methods](classes.md#override-methods)), `sealed` ([Sealed methods](classes.md#sealed-methods)), `abstract` ([Abstract methods](classes.md#abstract-methods)), and `extern` ([External methods](classes.md#external-methods)) modifiers.
Property declarations are subject to the same rules as method declarations ([Methods](classes.md#methods)) with regard to valid combinations of modifiers.
@ -2651,8 +2651,8 @@ The accessor declarations consist of a *get_accessor_declaration*, a *set_access
The use of *accessor_modifier*s is governed by the following restrictions:
* An *accessor_modifier* may not be used in an interface or in an explicit interface member implementation.
* For a property or indexer that has no `override` modifer, an *accessor_modifier* is permitted only if the property or indexer has both a `get` and `set` accessor, and then is permitted only on one of those accessors.
* For a property or indexer that includes an `override` modifer, an accessor must match the *accessor_modifier*, if any, of the accessor being overridden.
* For a property or indexer that has no `override` modifier, an *accessor_modifier* is permitted only if the property or indexer has both a `get` and `set` accessor, and then is permitted only on one of those accessors.
* For a property or indexer that includes an `override` modifier, an accessor must match the *accessor_modifier*, if any, of the accessor being overridden.
* The *accessor_modifier* must declare an accessibility that is strictly more restrictive than the declared accessibility of the property or indexer itself. To be precise:
* If the property or indexer has a declared accessibility of `public`, the *accessor_modifier* may be either `protected internal`, `internal`, `protected`, or `private`.
* If the property or indexer has a declared accessibility of `protected internal`, the *accessor_modifier* may be either `internal`, `protected`, or `private`.
@ -3114,7 +3114,7 @@ remove_accessor_declaration
;
```
An *event_declaration* may include a set of *attributes* ([Attributes](attributes.md#attributes)) and a valid combination of the four access modifiers ([Access modifiers](classes.md#access-modifiers)), the `new` ([The new modifier](classes.md#the-new-modifier)), `static` ([Static and instance methods](classes.md#static-and-instance-methods)), `virtual` ([Virtual methods](classes.md#virtual-methods)), `override` ([Override methods](classes.md#override-methods)), `sealed` ([Sealed methods](classes.md#sealed-methods)), `abstract` ([Abstract methods](classes.md#abstract-methods)), and `extern` ([External methods](classes.md#external-methods)) modifiers.
An *event_declaration* may include a set of *attributes* ([Attributes](attributes.md)) and a valid combination of the four access modifiers ([Access modifiers](classes.md#access-modifiers)), the `new` ([The new modifier](classes.md#the-new-modifier)), `static` ([Static and instance methods](classes.md#static-and-instance-methods)), `virtual` ([Virtual methods](classes.md#virtual-methods)), `override` ([Override methods](classes.md#override-methods)), `sealed` ([Sealed methods](classes.md#sealed-methods)), `abstract` ([Abstract methods](classes.md#abstract-methods)), and `extern` ([External methods](classes.md#external-methods)) modifiers.
Event declarations are subject to the same rules as method declarations ([Methods](classes.md#methods)) with regard to valid combinations of modifiers.
@ -3171,7 +3171,7 @@ Here, the `LoginDialog` instance constructor creates two `Button` instances and
### Field-like events
Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. To be used in this way, an event must not be `abstract` or `extern`, and must not explicitly include *event_accessor_declarations*. Such an event can be used in any context that permits a field. The field contains a delegate ([Delegates](delegates.md#delegates)) which refers to the list of event handlers that have been added to the event. If no event handlers have been added, the field contains `null`.
Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. To be used in this way, an event must not be `abstract` or `extern`, and must not explicitly include *event_accessor_declarations*. Such an event can be used in any context that permits a field. The field contains a delegate ([Delegates](delegates.md)) which refers to the list of event handlers that have been added to the event. If no event handlers have been added, the field contains `null`.
In the example
```csharp
@ -3348,7 +3348,7 @@ indexer_body
;
```
An *indexer_declaration* may include a set of *attributes* ([Attributes](attributes.md#attributes)) and a valid combination of the four access modifiers ([Access modifiers](classes.md#access-modifiers)), the `new` ([The new modifier](classes.md#the-new-modifier)), `virtual` ([Virtual methods](classes.md#virtual-methods)), `override` ([Override methods](classes.md#override-methods)), `sealed` ([Sealed methods](classes.md#sealed-methods)), `abstract` ([Abstract methods](classes.md#abstract-methods)), and `extern` ([External methods](classes.md#external-methods)) modifiers.
An *indexer_declaration* may include a set of *attributes* ([Attributes](attributes.md)) and a valid combination of the four access modifiers ([Access modifiers](classes.md#access-modifiers)), the `new` ([The new modifier](classes.md#the-new-modifier)), `virtual` ([Virtual methods](classes.md#virtual-methods)), `override` ([Override methods](classes.md#override-methods)), `sealed` ([Sealed methods](classes.md#sealed-methods)), `abstract` ([Abstract methods](classes.md#abstract-methods)), and `extern` ([External methods](classes.md#external-methods)) modifiers.
Indexer declarations are subject to the same rules as method declarations ([Methods](classes.md#methods)) with regard to valid combinations of modifiers, with the one exception being that the static modifier is not permitted on an indexer declaration.
@ -3767,7 +3767,7 @@ constructor_body
;
```
A *constructor_declaration* may include a set of *attributes* ([Attributes](attributes.md#attributes)), a valid combination of the four access modifiers ([Access modifiers](classes.md#access-modifiers)), and an `extern` ([External methods](classes.md#external-methods)) modifier. A constructor declaration is not permitted to include the same modifier multiple times.
A *constructor_declaration* may include a set of *attributes* ([Attributes](attributes.md)), a valid combination of the four access modifiers ([Access modifiers](classes.md#access-modifiers)), and an `extern` ([External methods](classes.md#external-methods)) modifier. A constructor declaration is not permitted to include the same modifier multiple times.
The *identifier* of a *constructor_declarator* must name the class in which the instance constructor is declared. If any other name is specified, a compile-time error occurs.
@ -4026,7 +4026,7 @@ static_constructor_body
;
```
A *static_constructor_declaration* may include a set of *attributes* ([Attributes](attributes.md#attributes)) and an `extern` modifier ([External methods](classes.md#external-methods)).
A *static_constructor_declaration* may include a set of *attributes* ([Attributes](attributes.md)) and an `extern` modifier ([External methods](classes.md#external-methods)).
The *identifier* of a *static_constructor_declaration* must name the class in which the static constructor is declared. If any other name is specified, a compile-time error occurs.
@ -4145,7 +4145,7 @@ destructor_body
;
```
A *destructor_declaration* may include a set of *attributes* ([Attributes](attributes.md#attributes)).
A *destructor_declaration* may include a set of *attributes* ([Attributes](attributes.md)).
The *identifier* of a *destructor_declaration* must name the class in which the destructor is declared. If any other name is specified, a compile-time error occurs.

View file

@ -39,7 +39,7 @@ However, dynamic conversions ([Implicit dynamic conversions](conversions.md#impl
An identity conversion converts from any type to the same type. This conversion exists such that an entity that already has a required type can be said to be convertible to that type.
* Because object and dynamic are considered equivalent there is an identity conversion between `object` and `dynamic`, and between constructed types that are the same when replacing all occurences of `dynamic` with `object`.
* Because object and dynamic are considered equivalent there is an identity conversion between `object` and `dynamic`, and between constructed types that are the same when replacing all occurrences of `dynamic` with `object`.
### Implicit numeric conversions
@ -503,9 +503,9 @@ An *anonymous_method_expression* or *lambda_expression* is classified as an anon
* If `F` does not contain an *anonymous_function_signature*, then `D` may have zero or more parameters of any type, as long as no parameter of `D` has the `out` parameter modifier.
* If `F` has an explicitly typed parameter list, each parameter in `D` has the same type and modifiers as the corresponding parameter in `F`.
* If `F` has an implicitly typed parameter list, `D` has no `ref` or `out` parameters.
* If the body of `F` is an expression, and either `D` has a `void` return type or `F` is async and `D` has the return type `Task`, then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid expression (wrt [Expressions](expressions.md#expressions)) that would be permitted as a *statement_expression* ([Expression statements](statements.md#expression-statements)).
* If the body of `F` is an expression, and either `D` has a `void` return type or `F` is async and `D` has the return type `Task`, then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid expression (wrt [Expressions](expressions.md)) that would be permitted as a *statement_expression* ([Expression statements](statements.md#expression-statements)).
* If the body of `F` is a statement block, and either `D` has a `void` return type or `F` is async and `D` has the return type `Task`, then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid statement block (wrt [Blocks](statements.md#blocks)) in which no `return` statement specifies an expression.
* If the body of `F` is an expression, and *either* `F` is non-async and `D` has a non-void return type `T`, *or* `F` is async and `D` has a return type `Task<T>`, then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid expression (wrt [Expressions](expressions.md#expressions)) that is implicitly convertible to `T`.
* If the body of `F` is an expression, and *either* `F` is non-async and `D` has a non-void return type `T`, *or* `F` is async and `D` has a return type `Task<T>`, then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid expression (wrt [Expressions](expressions.md)) that is implicitly convertible to `T`.
* If the body of `F` is a statement block, and *either* `F` is non-async and `D` has a non-void return type `T`, *or* `F` is async and `D` has a return type `Task<T>`, then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid statement block (wrt [Blocks](statements.md#blocks)) with a non-reachable end point in which each `return` statement specifies an expression that is implicitly convertible to `T`.
For the purpose of brevity, this section uses the short form for the task types `Task` and `Task<T>` ([Async functions](classes.md#async-functions)).

View file

@ -41,7 +41,7 @@ The optional *variant_type_parameter_list* ([Variant type parameter lists](inter
The return type of a delegate type must be either `void`, or output-safe ([Variance safety](interfaces.md#variance-safety)).
All the formal parameter types of a delegate type must be input-safe. Additionally, any `out` or `ref` parameter types must also be output-safe. Note that even `out` parameters are required to be input-safe, due to a limitiation of the underlying execution platform.
All the formal parameter types of a delegate type must be input-safe. Additionally, any `out` or `ref` parameter types must also be output-safe. Note that even `out` parameters are required to be input-safe, due to a limitation of the underlying execution platform.
Delegate types in C# are name equivalent, not structurally equivalent. Specifically, two different delegate types that have the same parameter lists and return type are considered different delegate types. However, instances of two distinct but structurally equivalent delegate types may compare as equal ([Delegate equality operators](expressions.md#delegate-equality-operators)).

View file

@ -292,7 +292,7 @@ __Example:__
```csharp
/// <summary>This is the entry point of the Point class testing program.
/// <para>This program tests each method and operator, and
/// is intended to be run after any non-trvial maintenance has
/// is intended to be run after any non-trivial maintenance has
/// been performed on the Point class.</para></summary>
public static void Main() {
// ...
@ -994,7 +994,7 @@ public class Point
/// <summary>This is the entry point of the Point class testing
/// program.
/// <para>This program tests each method and operator, and
/// is intended to be run after any non-trvial maintenance has
/// is intended to be run after any non-trivial maintenance has
/// been performed on the Point class.</para></summary>
public static void Main() {
// class test code goes here
@ -1121,7 +1121,7 @@ Here is the output produced by one documentation generator when given the source
<summary>This is the entry point of the Point class testing
program.
<para>This program tests each method and operator, and
is intended to be run after any non-trvial maintenance has
is intended to be run after any non-trivial maintenance has
been performed on the Point class.</para></summary>
</member>

View file

@ -44,7 +44,7 @@ The following operations in C# are subject to binding:
* Member access: `e.M`
* Method invocation: `e.M(e1, ..., eN)`
* Delegate invocaton:`e(e1, ..., eN)`
* Delegate invocation:`e(e1, ..., eN)`
* Element access: `e[e1, ..., eN]`
* Object creation: `new C(e1, ..., eN)`
* Overloaded unary operators: `+`, `-`, `!`, `~`, `++`, `--`, `true`, `false`
@ -117,7 +117,7 @@ The following table summarizes all operators in order of precedence from highest
| __Section__ | __Category__ | __Operators__ |
|-----------------------------------------------------------------------------------------------|-----------------------------|---------------|
| [Primary expressions](expressions.md#primary-expressions) | Primary | `x.y` `f(x)` `a[x]` `x++` `x--` `new` `typeof` `default` `checked` `unchecked` `delegate` |
| [Unary operators](expressions.md#unary-operators) | Unary | `+` `*` `!` `~` `++x` `--x` `(T)x` |
| [Unary operators](expressions.md#unary-operators) | Unary | `+` `-` `!` `~` `++x` `--x` `(T)x` |
| [Arithmetic operators](expressions.md#arithmetic-operators) | Multiplicative | `*` `/` `%` |
| [Arithmetic operators](expressions.md#arithmetic-operators) | Additive | `+` `-` |
| [Shift operators](expressions.md#shift-operators) | Shift | `<<` `>>` |
@ -300,7 +300,7 @@ decimal AddPercent(decimal x, double percent) {
A member lookup is the process whereby the meaning of a name in the context of a type is determined. A member lookup can occur as part of evaluating a *simple_name* ([Simple names](expressions.md#simple-names)) or a *member_access* ([Member access](expressions.md#member-access)) in an expression. If the *simple_name* or *member_access* occurs as the *primary_expression* of an *invocation_expression* ([Method invocations](expressions.md#method-invocations)), the member is said to be invoked.
If a member is a method or event, or if it is a constant, field or property of either a delegate type ([Delegates](delegates.md#delegates)) or the type `dynamic` ([The dynamic type](types.md#the-dynamic-type)), then the member is said to be *invocable*.
If a member is a method or event, or if it is a constant, field or property of either a delegate type ([Delegates](delegates.md)) or the type `dynamic` ([The dynamic type](types.md#the-dynamic-type)), then the member is said to be *invocable*.
Member lookup considers not only the name of a member but also the number of type parameters the member has and whether the member is accessible. For the purposes of member lookup, generic methods and nested generic types have the number of type parameters indicated in their respective declarations and all other members have zero type parameters.
@ -879,7 +879,7 @@ interface I2<T> {...}
class G1<U>
{
int F1(U u); // Overload resulotion for G<int>.F1
int F1(U u); // Overload resolution for G<int>.F1
int F1(int i); // will pick non-generic
void F2(I1<U> a); // Valid overload
@ -971,7 +971,7 @@ primary_expression
primary_no_array_creation_expression
: literal
| interpolated_string
| interpolated_string_expression
| simple_name
| parenthesized_expression
| member_access
@ -1093,7 +1093,7 @@ A *simple_name* is either of the form `I` or of the form `I<A1,...,Ak>`, where `
* Otherwise, the *namespace_or_type_name* refers to the type constructed with the given type arguments.
* Otherwise, if the location where the *simple_name* occurs is enclosed by a namespace declaration for `N`:
* If `K` is zero and the namespace declaration contains an *extern_alias_directive* or *using_alias_directive* that associates the name `I` with an imported namespace or type, then the *simple_name* refers to that namespace or type.
* Otherwise, if the namespaces and type declarations imported by the *using_namespace_directive*s and *using_static_directive*s of the namespace declaration contain exactly one accessible type or non-extension static membre having name `I` and `K` type parameters, then the *simple_name* refers to that type or member constructed with the given type arguments.
* Otherwise, if the namespaces and type declarations imported by the *using_namespace_directive*s and *using_static_directive*s of the namespace declaration contain exactly one accessible type or non-extension static member having name `I` and `K` type parameters, then the *simple_name* refers to that type or member constructed with the given type arguments.
* Otherwise, if the namespaces and types imported by the *using_namespace_directive*s of the namespace declaration contain more than one accessible type or non-extension-method static member having name `I` and `K` type parameters, then the *simple_name* is ambiguous and an error occurs.
Note that this entire step is exactly parallel to the corresponding step in the processing of a *namespace_or_type_name* ([Namespace and type names](basic-concepts.md#namespace-and-type-names)).
@ -1407,7 +1407,7 @@ E.F(1)
D.G(2)
C.H(3)
```
`D.G` takes precendece over `C.G`, and `E.F` takes precedence over both `D.F` and `C.F`.
`D.G` takes precedence over `C.G`, and `E.F` takes precedence over both `D.F` and `C.F`.
#### Delegate invocations
@ -2769,7 +2769,7 @@ The predefined division operators are listed below. The operators all compute th
decimal operator /(decimal x, decimal y);
```
If the value of the right operand is zero, a `System.DivideByZeroException` is thrown. If the resulting value is too large to represent in the `decimal` format, a `System.OverflowException` is thrown. If the result value is too small to represent in the `decimal` format, the result is zero. The scale of the result is the smallest scale that will preserve a result equal to the nearest representantable decimal value to the true mathematical result.
If the value of the right operand is zero, a `System.DivideByZeroException` is thrown. If the resulting value is too large to represent in the `decimal` format, a `System.OverflowException` is thrown. If the result value is too small to represent in the `decimal` format, the result is zero. The scale of the result is the smallest scale that will preserve a result equal to the nearest representable decimal value to the true mathematical result.
Decimal division is equivalent to using the division operator of type `System.Decimal`.
@ -3864,7 +3864,7 @@ static void F() {
}
```
When not captured, there is no way to observe exactly how often a local variable is instantiated—because the lifetimes of the instantiations are disjoint, it is possible for each instantation to simply use the same storage location. However, when an anonymous function captures a local variable, the effects of instantiation become apparent.
When not captured, there is no way to observe exactly how often a local variable is instantiated—because the lifetimes of the instantiations are disjoint, it is possible for each instantiation to simply use the same storage location. However, when an anonymous function captures a local variable, the effects of instantiation become apparent.
The example
```csharp
@ -4070,7 +4070,7 @@ A query expression begins with a `from` clause and ends with either a `select` o
Query expressions contain a number of "contextual keywords", i.e., identifiers that have special meaning in a given context. Specifically these are `from`, `where`, `join`, `on`, `equals`, `into`, `let`, `orderby`, `ascending`, `descending`, `select`, `group` and `by`. In order to avoid ambiguities in query expressions caused by mixed use of these identifiers as keywords or simple names, these identifiers are considered keywords when occurring anywhere within a query expression.
For this purpose, a query expression is any expression that starts with "`from dentifier`" followed by any token except "`;`", "`=`" or "`,`".
For this purpose, a query expression is any expression that starts with "`from identifier`" followed by any token except "`;`", "`=`" or "`,`".
In order to use these words as identifiers within a query expression, they can be prefixed with "`@`" ([Identifiers](lexical-structure.md#identifiers)).
@ -4859,7 +4859,7 @@ Constant expressions occur in the contexts listed below. In these contexts, a co
* `case` labels of a `switch` statement ([The switch statement](statements.md#the-switch-statement)).
* `goto case` statements ([The goto statement](statements.md#the-goto-statement)).
* Dimension lengths in an array creation expression ([Array creation expressions](expressions.md#array-creation-expressions)) that includes an initializer.
* Attributes ([Attributes](attributes.md#attributes)).
* Attributes ([Attributes](attributes.md)).
An implicit constant expression conversion ([Implicit constant expression conversions](conversions.md#implicit-constant-expression-conversions)) permits a constant expression of type `int` to be converted to `sbyte`, `byte`, `short`, `ushort`, `uint`, or `ulong`, provided the value of the constant expression is within the range of the destination type.

View file

@ -16,7 +16,7 @@ interface_declaration
;
```
An *interface_declaration* consists of an optional set of *attributes* ([Attributes](attributes.md#attributes)), followed by an optional set of *interface_modifier*s ([Interface modifiers](interfaces.md#interface-modifiers)), followed by an optional `partial` modifier, followed by the keyword `interface` and an *identifier* that names the interface, followed by an optional *variant_type_parameter_list* specification ([Variant type parameter lists](interfaces.md#variant-type-parameter-lists)), followed by an optional *interface_base* specification ([Base interfaces](interfaces.md#base-interfaces)), followed by an optional *type_parameter_constraints_clause*s specification ([Type parameter constraints](classes.md#type-parameter-constraints)), followed by an *interface_body* ([Interface body](interfaces.md#interface-body)), optionally followed by a semicolon.
An *interface_declaration* consists of an optional set of *attributes* ([Attributes](attributes.md)), followed by an optional set of *interface_modifier*s ([Interface modifiers](interfaces.md#interface-modifiers)), followed by an optional `partial` modifier, followed by the keyword `interface` and an *identifier* that names the interface, followed by an optional *variant_type_parameter_list* specification ([Variant type parameter lists](interfaces.md#variant-type-parameter-lists)), followed by an optional *interface_base* specification ([Base interfaces](interfaces.md#base-interfaces)), followed by an optional *type_parameter_constraints_clause*s specification ([Type parameter constraints](classes.md#type-parameter-constraints)), followed by an *interface_body* ([Interface body](interfaces.md#interface-body)), optionally followed by a semicolon.
### Interface modifiers
@ -218,7 +218,7 @@ The *attributes*, *return_type*, *identifier*, and *formal_parameter_list* of an
Each formal parameter type of an interface method must be input-safe ([Variance safety](interfaces.md#variance-safety)), and the return type must be either `void` or output-safe. Furthermore, each class type constraint, interface type constraint and type parameter constraint on any type parameter of the method must be input-safe.
These rules ensure that any covariant or contravariant usage of the interface remains typesafe. For example,
These rules ensure that any covariant or contravariant usage of the interface remains type-safe. For example,
```csharp
interface I<out T> { void M<U>() where U : T; }
```
@ -287,7 +287,7 @@ The *attributes*, *type*, and *formal_parameter_list* of an interface indexer de
The accessors of an interface indexer declaration correspond to the accessors of a class indexer declaration ([Indexers](classes.md#indexers)), except that the accessor body must always be a semicolon. Thus, the accessors simply indicate whether the indexer is read-write, read-only, or write-only.
All the formal parameter types of an interface indexer must be input-safe . In addition, any `out` or `ref` formal parameter types must also be output-safe. Note that even `out` parameters are required to be input-safe, due to a limitiation of the underlying execution platform.
All the formal parameter types of an interface indexer must be input-safe . In addition, any `out` or `ref` formal parameter types must also be output-safe. Note that even `out` parameters are required to be input-safe, due to a limitation of the underlying execution platform.
The type of an interface indexer must be output-safe if there is a get accessor, and must be input-safe if there is a set accessor.

View file

@ -1551,7 +1551,7 @@ An instance of the `Function` delegate type can reference any method that takes
A delegate can reference either a static method (such as `Square` or `Math.Sin` in the previous example) or an instance method (such as `m.Multiply` in the previous example). A delegate that references an instance method also references a particular object, and when the instance method is invoked through the delegate, that object becomes `this` in the invocation.
Delegates can also be created using anonymous functions, which are "inline methods" that are created on the fly. Anonymous functions can see the local variables of the sourrounding methods. Thus, the multiplier example above can be written more easily without using a `Multiplier` class:
Delegates can also be created using anonymous functions, which are "inline methods" that are created on the fly. Anonymous functions can see the local variables of the surrounding methods. Thus, the multiplier example above can be written more easily without using a `Multiplier` class:
```csharp
double[] doubles = Apply(a, (double x) => x * 2.0);

View file

@ -346,7 +346,7 @@ keyword
;
```
In some places in the grammar, specific identifiers have special meaning, but are not keywords. Such identifiers are sometimes referred to as "contextual keywords". For example, within a property declaration, the "`get`" and "`set`" identifiers have special meaning ([Accessors](classes.md#accessors)). An identifier other than `get` or `set` is never permitted in these locations, so this use does not conflict with a use of these words as identifiers. In other cases, such as with the identifier "`var`" in implicitly typed local variable declarations ([Local variable declarations](statements.md#local-variable-declarations)), a contectual keyword can conflict with declared names. In such cases, the declared name takes precedence over the use of the identifier as a contextual keyword.
In some places in the grammar, specific identifiers have special meaning, but are not keywords. Such identifiers are sometimes referred to as "contextual keywords". For example, within a property declaration, the "`get`" and "`set`" identifiers have special meaning ([Accessors](classes.md#accessors)). An identifier other than `get` or `set` is never permitted in these locations, so this use does not conflict with a use of these words as identifiers. In other cases, such as with the identifier "`var`" in implicitly typed local variable declarations ([Local variable declarations](statements.md#local-variable-declarations)), a contextual keyword can conflict with declared names. In such cases, the declared name takes precedence over the use of the identifier as a contextual keyword.
### Literals
@ -774,8 +774,8 @@ single_verbatim_balanced_text_character
An *interpolated_string_literal* token is reinterpreted as multiple tokens and other input elements as follows, in order of occurrence in the *interpolated_string_literal*:
* Occurences of the following are reinterpreted as separate individual tokens: the leading `$` sign, *interpolated_regular_string_whole*, *interpolated_regular_string_start*, *interpolated_regular_string_mid*, *interpolated_regular_string_end*, *interpolated_verbatim_string_whole*, *interpolated_verbatim_string_start*, *interpolated_verbatim_string_mid* and *interpolated_verbatim_string_end*.
* Occurences of *regular_balanced_text* and *verbatim_balanced_text* between these are reprocessed as an *input_section* ([Lexical analysis](lexical-structure.md#lexical-analysis)) and are reinterpreted as the resulting sequence of input elements. These may in turn include interpolated string literal tokens to be reinterpreted.
* Occurrences of the following are reinterpreted as separate individual tokens: the leading `$` sign, *interpolated_regular_string_whole*, *interpolated_regular_string_start*, *interpolated_regular_string_mid*, *interpolated_regular_string_end*, *interpolated_verbatim_string_whole*, *interpolated_verbatim_string_start*, *interpolated_verbatim_string_mid* and *interpolated_verbatim_string_end*.
* Occurrences of *regular_balanced_text* and *verbatim_balanced_text* between these are reprocessed as an *input_section* ([Lexical analysis](lexical-structure.md#lexical-analysis)) and are reinterpreted as the resulting sequence of input elements. These may in turn include interpolated string literal tokens to be reinterpreted.
Syntactic analysis will recombine the tokens into an *interpolated_string_expression* ([Interpolated strings](expressions.md#interpolated-strings)).

View file

@ -18,7 +18,7 @@ A C# program consists of one or more compilation units, each contained in a sepa
The *using_directive*s of a compilation unit affect the *global_attributes* and *namespace_member_declaration*s of that compilation unit, but have no effect on other compilation units.
The *global_attributes* ([Attributes](attributes.md#attributes)) of a compilation unit permit the specification of attributes for the target assembly and module. Assemblies and modules act as physical containers for types. An assembly may consist of several physically separate modules.
The *global_attributes* ([Attributes](attributes.md)) of a compilation unit permit the specification of attributes for the target assembly and module. Assemblies and modules act as physical containers for types. An assembly may consist of several physically separate modules.
The *namespace_member_declaration*s of each compilation unit of a program contribute members to a single declaration space called the global namespace. For example:

View file

@ -17,7 +17,7 @@ struct_declaration
;
```
A *struct_declaration* consists of an optional set of *attributes* ([Attributes](attributes.md#attributes)), followed by an optional set of *struct_modifier*s ([Struct modifiers](structs.md#struct-modifiers)), followed by an optional `partial` modifier, followed by the keyword `struct` and an *identifier* that names the struct, followed by an optional *type_parameter_list* specification ([Type parameters](classes.md#type-parameters)), followed by an optional *struct_interfaces* specification ([Partial modifier](structs.md#partial-modifier)) ), followed by an optional *type_parameter_constraints_clause*s specification ([Type parameter constraints](classes.md#type-parameter-constraints)), followed by a *struct_body* ([Struct body](structs.md#struct-body)), optionally followed by a semicolon.
A *struct_declaration* consists of an optional set of *attributes* ([Attributes](attributes.md)), followed by an optional set of *struct_modifier*s ([Struct modifiers](structs.md#struct-modifiers)), followed by an optional `partial` modifier, followed by the keyword `struct` and an *identifier* that names the struct, followed by an optional *type_parameter_list* specification ([Type parameters](classes.md#type-parameters)), followed by an optional *struct_interfaces* specification ([Partial modifier](structs.md#partial-modifier)) ), followed by an optional *type_parameter_constraints_clause*s specification ([Type parameter constraints](classes.md#type-parameter-constraints)), followed by a *struct_body* ([Struct body](structs.md#struct-body)), optionally followed by a semicolon.
### Struct modifiers

View file

@ -309,7 +309,7 @@ A reference type value is a reference to an ***instance*** of the type, the latt
A class type defines a data structure that contains data members (constants and fields), function members (methods, properties, events, indexers, operators, instance constructors, destructors and static constructors), and nested types. Class types support inheritance, a mechanism whereby derived classes can extend and specialize base classes. Instances of class types are created using *object_creation_expression*s ([Object creation expressions](expressions.md#object-creation-expressions)).
Class types are described in [Classes](classes.md#classes).
Class types are described in [Classes](classes.md).
Certain predefined class types have special meaning in the C# language, as described in the table below.
@ -319,10 +319,10 @@ Certain predefined class types have special meaning in the C# language, as descr
| `System.Object` | The ultimate base class of all other types. See [The object type](types.md#the-object-type). |
| `System.String` | The string type of the C# language. See [The string type](types.md#the-string-type). |
| `System.ValueType` | The base class of all value types. See [The System.ValueType type](types.md#the-systemvaluetype-type). |
| `System.Enum` | The base class of all enum types. See [Enums](enums.md#enums). |
| `System.Array` | The base class of all array types. See [Arrays](arrays.md#arrays). |
| `System.Delegate` | The base class of all delegate types. See [Delegates](delegates.md#delegates). |
| `System.Exception` | The base class of all exception types. See [Exceptions](exceptions.md#exceptions). |
| `System.Enum` | The base class of all enum types. See [Enums](enums.md). |
| `System.Array` | The base class of all array types. See [Arrays](arrays.md). |
| `System.Delegate` | The base class of all delegate types. See [Delegates](delegates.md). |
| `System.Exception` | The base class of all exception types. See [Exceptions](exceptions.md). |
### The object type
@ -361,13 +361,13 @@ The keyword `string` is simply an alias for the predefined class `System.String`
An interface defines a contract. A class or struct that implements an interface must adhere to its contract. An interface may inherit from multiple base interfaces, and a class or struct may implement multiple interfaces.
Interface types are described in [Interfaces](interfaces.md#interfaces).
Interface types are described in [Interfaces](interfaces.md).
### Array types
An array is a data structure that contains zero or more variables which are accessed through computed indices. The variables contained in an array, also called the elements of the array, are all of the same type, and this type is called the element type of the array.
Array types are described in [Arrays](arrays.md#arrays).
Array types are described in [Arrays](arrays.md).
### Delegate types
@ -375,7 +375,7 @@ A delegate is a data structure that refers to one or more methods. For instance
The closest equivalent of a delegate in C or C++ is a function pointer, but whereas a function pointer can only reference static functions, a delegate can reference both static and instance methods. In the latter case, the delegate stores not only a reference to the method's entry point, but also a reference to the object instance on which to invoke the method.
Delegate types are described in [Delegates](delegates.md#delegates).
Delegate types are described in [Delegates](delegates.md).
## Boxing and unboxing
@ -541,7 +541,7 @@ type_argument
;
```
In unsafe code ([Unsafe code](unsafe-code.md#unsafe-code)), a *type_argument* may not be a pointer type. Each type argument must satisfy any constraints on the corresponding type parameter ([Type parameter constraints](classes.md#type-parameter-constraints)).
In unsafe code ([Unsafe code](unsafe-code.md)), a *type_argument* may not be a pointer type. Each type argument must satisfy any constraints on the corresponding type parameter ([Type parameter constraints](classes.md#type-parameter-constraints)).
### Open and closed types
@ -626,11 +626,11 @@ As a type, type parameters are purely a compile-time construct. At run-time, eac
If a conversion exists from a lambda expression to a delegate type `D`, a conversion also exists to the expression tree type `Expression<D>`. Whereas the conversion of a lambda expression to a delegate type generates a delegate that references executable code for the lambda expression, conversion to an expression tree type creates an expression tree representation of the lambda expression.
Expression trees are efficient in-memory data representations of lambda expressionsand make the structure of the lambda expressiontransparent and explicit.
Expression trees are efficient in-memory data representations of lambda expressions and make the structure of the lambda expression transparent and explicit.
Just like a delegate type `D`, `Expression<D>` is said to have parameter and return types, which are the same as those of `D`.
The following example represents a lambda expressionboth as executable code and as an expression tree. Because a conversion exists to `Func<int,int>`, a conversion also exists to `Expression<Func<int,int>>`:
The following example represents a lambda expression both as executable code and as an expression tree. Because a conversion exists to `Func<int,int>`, a conversion also exists to `Expression<Func<int,int>>`:
```csharp
Func<int,int> del = x => x + 1; // Code
@ -640,7 +640,7 @@ Expression<Func<int,int>> exp = x => x + 1; // Data
Following these assignments, the delegate `del` references a method that returns `x + 1`, and the expression tree `exp` references a data structure that describes the expression `x => x + 1`.
The exact definition of the generic type `Expression<D>` as well as the precise rules for constructing an expression tree when a lambda expressionis converted to an expression tree type, are both outside the scope of this specification.
The exact definition of the generic type `Expression<D>` as well as the precise rules for constructing an expression tree when a lambda expression is converted to an expression tree type, are both outside the scope of this specification.
Two things are important to make explicit:

View file

@ -151,7 +151,7 @@ Here, because `F`'s signature includes a pointer type, it can only be written in
## Pointer types
In an unsafe context, a *type* ([Types](types.md#types)) may be a *pointer_type* as well as a *value_type* or a *reference_type*. However, a *pointer_type* may also be used in a `typeof` expression ([Anonymous object creation expressions](expressions.md#anonymous-object-creation-expressions)) outside of an unsafe context as such usage is not unsafe.
In an unsafe context, a *type* ([Types](types.md)) may be a *pointer_type* as well as a *value_type* or a *reference_type*. However, a *pointer_type* may also be used in a `typeof` expression ([Anonymous object creation expressions](expressions.md#anonymous-object-creation-expressions)) outside of an unsafe context as such usage is not unsafe.
```antlr
type_unsafe
@ -679,7 +679,7 @@ When applied to an operand that has struct type, the result is the total number
## The fixed statement
In an unsafe context, the *embedded_statement* ([Statements](statements.md#statements)) production permits an additional construct, the `fixed` statement, which is used to "fix" a moveable variable such that its address remains constant for the duration of the statement.
In an unsafe context, the *embedded_statement* ([Statements](statements.md)) production permits an additional construct, the `fixed` statement, which is used to "fix" a moveable variable such that its address remains constant for the duration of the statement.
```antlr
fixed_statement
@ -898,7 +898,7 @@ fixed_size_buffer_declarator
;
```
A fixed size buffer declaration may include a set of attributes ([Attributes](attributes.md#attributes)), a `new` modifier ([Modifiers](classes.md#modifiers)), a valid combination of the four access modifiers ([Type parameters and constraints](classes.md#type-parameters-and-constraints)) and an `unsafe` modifier ([Unsafe contexts](unsafe-code.md#unsafe-contexts)). The attributes and modifiers apply to all of the members declared by the fixed size buffer declaration. It is an error for the same modifier to appear multiple times in a fixed size buffer declaration.
A fixed size buffer declaration may include a set of attributes ([Attributes](attributes.md)), a `new` modifier ([Modifiers](classes.md#modifiers)), a valid combination of the four access modifiers ([Type parameters and constraints](classes.md#type-parameters-and-constraints)) and an `unsafe` modifier ([Unsafe contexts](unsafe-code.md#unsafe-contexts)). The attributes and modifiers apply to all of the members declared by the fixed size buffer declaration. It is an error for the same modifier to appear multiple times in a fixed size buffer declaration.
A fixed size buffer declaration is not permitted to include the `static` modifier.
@ -908,7 +908,7 @@ The buffer element type is followed by a list of fixed size buffer declarators,
The elements of a fixed size buffer are guaranteed to be laid out sequentially in memory.
A fixed size buffer declaration that declares multiple fixed size buffers is equivalent to multiple declarations of a single fixed size buffer declation with the same attributes, and element types. For example
A fixed size buffer declaration that declares multiple fixed size buffers is equivalent to multiple declarations of a single fixed size buffer declaration with the same attributes, and element types. For example
```csharp
unsafe struct A

View file

@ -105,7 +105,7 @@ If the parent *block*, *for_statement*, *switch_statement*, *using_statement*, *
A local variable introduced by a *local_variable_declaration* is not automatically initialized and thus has no default value. For the purpose of definite assignment checking, a local variable introduced by a *local_variable_declaration* is considered initially unassigned. A *local_variable_declaration* may include a *local_variable_initializer*, in which case the variable is considered definitely assigned only after the initializing expression ([Declaration statements](variables.md#declaration-statements)).
Within the scope of a local variableintroduced by a *local_variable_declaration*, it is a compile-time error to refer to that local variable in a textual position that precedes its *local_variable_declarator*. If the local variable declaration is implicit ([Local variable declarations](statements.md#local-variable-declarations)), it is also an error to refer to the variable within its *local_variable_declarator*.
Within the scope of a local variable introduced by a *local_variable_declaration*, it is a compile-time error to refer to that local variable in a textual position that precedes its *local_variable_declarator*. If the local variable declaration is implicit ([Local variable declarations](statements.md#local-variable-declarations)), it is also an error to refer to the variable within its *local_variable_declarator*.
A local variable introduced by a *foreach_statement* or a *specific_catch_clause* is considered definitely assigned in its entire scope.
@ -447,8 +447,7 @@ The following rule applies to these kinds of expressions: literals ([Literals](e
#### General rules for expressions with embedded expressions
The following rules apply to these kinds of expressions: parenthesized expressions ([Parenthesized expressions](expressions.md#parenthesized-expressions)), element access expressions ([Element access](expressions.md#element-access)), base access expressions with indexing ([Base access](expressions.md#base-access)), increment and decrement expressions ([Postfix increment and decrement operators](expressions.md#postfix-increment-and-decrement-operators), [Prefix increment and decrement operators](expressions.md#prefix-increment-and-decrement-operators)), cast expressions ([Cast expressions](expressions.md#cast-expressions)), unary `+`, `-`, `~`, `*` expressions, binary `+`, `-`, `*`, `/`, `%`, `<<`, `>
>`, `<`, `<=`, `>`, `>=`, `==`, `!=`, `is`, `as`, `&`, `|`, `^` expressions ([Arithmetic operators](expressions.md#arithmetic-operators), [Shift operators](expressions.md#shift-operators), [Relational and type-testing operators](expressions.md#relational-and-type-testing-operators), [Logical operators](expressions.md#logical-operators)), compound assignment expressions ([Compound assignment](expressions.md#compound-assignment)), `checked` and `unchecked` expressions ([The checked and unchecked operators](expressions.md#the-checked-and-unchecked-operators)), plus array and delegate creation expressions ([The new operator](expressions.md#the-new-operator)).
The following rules apply to these kinds of expressions: parenthesized expressions ([Parenthesized expressions](expressions.md#parenthesized-expressions)), element access expressions ([Element access](expressions.md#element-access)), base access expressions with indexing ([Base access](expressions.md#base-access)), increment and decrement expressions ([Postfix increment and decrement operators](expressions.md#postfix-increment-and-decrement-operators), [Prefix increment and decrement operators](expressions.md#prefix-increment-and-decrement-operators)), cast expressions ([Cast expressions](expressions.md#cast-expressions)), unary `+`, `-`, `~`, `*` expressions, binary `+`, `-`, `*`, `/`, `%`, `<<`, `>>`, `<`, `<=`, `>`, `>=`, `==`, `!=`, `is`, `as`, `&`, `|`, `^` expressions ([Arithmetic operators](expressions.md#arithmetic-operators), [Shift operators](expressions.md#shift-operators), [Relational and type-testing operators](expressions.md#relational-and-type-testing-operators), [Logical operators](expressions.md#logical-operators)), compound assignment expressions ([Compound assignment](expressions.md#compound-assignment)), `checked` and `unchecked` expressions ([The checked and unchecked operators](expressions.md#the-checked-and-unchecked-operators)), plus array and delegate creation expressions ([The new operator](expressions.md#the-new-operator)).
Each of these expressions has one or more sub-expressions that are unconditionally evaluated in a fixed order. For example, the binary `%` operator evaluates the left hand side of the operator, then the right hand side. An indexing operation evaluates the indexed expression, and then evaluates each of the index expressions, in order from left to right. For an expression *expr*, which has sub-expressions *e1, e2, ..., eN*, evaluated in that order: