Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create inline-arrays.md #7064

Merged
merged 39 commits into from
Apr 14, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
47f5c9a
Create safe-fixed-sized-buffers.md
AlekseyTs Mar 16, 2023
1e15e61
Update safe-fixed-sized-buffers.md
AlekseyTs Mar 16, 2023
6ca5ef5
Apply suggestions from code review
AlekseyTs Mar 16, 2023
ecaa0c6
Update safe-fixed-sized-buffers.md
AlekseyTs Mar 16, 2023
178356d
Update safe-fixed-sized-buffers.md
AlekseyTs Mar 16, 2023
ffffc7b
Update safe-fixed-sized-buffers.md
AlekseyTs Mar 16, 2023
6604a84
Delete fixed-sized-buffers.md
AlekseyTs Mar 16, 2023
26e058c
Create fixed-sized-buffers.md
AlekseyTs Mar 16, 2023
38d3217
Rename safe-fixed-sized-buffers.md to safe-fixed-size-buffers.md
AlekseyTs Mar 17, 2023
f92dadb
Update safe-fixed-size-buffers.md
AlekseyTs Mar 17, 2023
7d40dc1
Update safe-fixed-size-buffers.md
AlekseyTs Mar 18, 2023
79099cd
PR feedback
AlekseyTs Mar 20, 2023
a3ffcad
Add an alternative design
AlekseyTs Mar 21, 2023
8b8a86a
Update safe-fixed-size-buffers.md
AlekseyTs Mar 21, 2023
2924ee1
Update safe-fixed-size-buffers.md
AlekseyTs Mar 22, 2023
1426407
Update safe-fixed-size-buffers.md
AlekseyTs Mar 22, 2023
77e6a30
Update safe-fixed-size-buffers.md
AlekseyTs Mar 23, 2023
f793250
Apply suggestions from code review
AlekseyTs Mar 28, 2023
211cad3
Swap design options in safe-fixed-size-buffers.md
AlekseyTs Mar 31, 2023
b57d7e3
PR feedback for safe-fixed-size-buffers.md
AlekseyTs Mar 31, 2023
368c395
Clarify "Element access" section in safe-fixed-size-buffers.md
AlekseyTs Mar 31, 2023
6df9506
Update safe-fixed-size-buffers.md
AlekseyTs Mar 31, 2023
1b85bcd
Clarify "Conversions" section in safe-fixed-size-buffers.md
AlekseyTs Mar 31, 2023
5d23036
Update safe-fixed-size-buffers.md
AlekseyTs Mar 31, 2023
9b8924a
Clarifying "Fixed-size buffers in expressions" section in safe-fixed-…
AlekseyTs Mar 31, 2023
c4d683d
Update safe-fixed-size-buffers.md
AlekseyTs Apr 1, 2023
b7acdb8
Update safe-fixed-size-buffers.md
AlekseyTs Apr 1, 2023
a9abf26
Update safe-fixed-size-buffers.md
AlekseyTs Apr 3, 2023
884c33a
Add nn alternative to relying on AsSpan/AsReadOnlySpan helpers to saf…
AlekseyTs Apr 4, 2023
39e641b
Don't use pointers
AlekseyTs Apr 4, 2023
bda99a6
Update primary-constructors.md based on recent LDM decisions (#7099)
AlekseyTs Apr 7, 2023
30cc4ad
Revert "Update primary-constructors.md based on recent LDM decisions …
AlekseyTs Apr 7, 2023
e264575
Move "Detailed Design (Option 2)" to the "Alternatives" section in sa…
AlekseyTs Apr 11, 2023
938151a
Update safe-fixed-size-buffers.md
AlekseyTs Apr 11, 2023
0858400
Update safe-fixed-size-buffers.md
AlekseyTs Apr 11, 2023
fc9b629
Rename safe-fixed-size-buffers.md to inline-array.md
AlekseyTs Apr 14, 2023
b01c730
Rename feature to "Inline Array" from "Safe Fixed Size Buffers" in i…
AlekseyTs Apr 14, 2023
8606658
Rename inline-array.md to inline-arrays.md
AlekseyTs Apr 14, 2023
874228a
Update inline-arrays.md
AlekseyTs Apr 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,4 @@ Manually declare your structures and use unsafe code to construct indexers.

## Design meetings

Link to design notes that affect this proposal, and describe in one sentence for each what changes they led to.
Link to design notes that affect this proposal, and describe in one sentence for each what changes they led to.
140 changes: 140 additions & 0 deletions proposals/safe-fixed-size-buffers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
Safe Fixed Size Buffers
=====

## Summary

Provide a general-purpose and safe mechanism for declaring fixed sized buffers within C# classes, structs, and interfaces.

## Motivation

This proposal plans to address the many limitations of https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/unsafe-code.md#228-fixed-size-buffers.
Specifically it aims to allow the declaration of safe `fixed` buffers for managed and unmanaged types in a `struct`, `class`, or `interface`, and provide language safety verification for them.

## Detailed Design

The grammar for *variable_declarator* in https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#145-fields
will be extended to allow specifying the size of the buffer:

``` antlr
field_declaration
: attributes? field_modifier* type variable_declarators ';'
;

field_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'static'
| 'readonly'
| 'volatile'
| unsafe_modifier // unsafe code support
;

variable_declarators
: variable_declarator (',' variable_declarator)*
;

variable_declarator
: identifier ('=' variable_initializer)?
| fixed_size_buffer_declarator
AlekseyTs marked this conversation as resolved.
Show resolved Hide resolved
;

fixed_size_buffer_declarator
AlekseyTs marked this conversation as resolved.
Show resolved Hide resolved
: identifier '[' constant_expression ']'
;
```

A *fixed_size_buffer_declarator* introduces a fixed-size buffer of a given element type.

The buffer element type is the *type* specified in `field_declaration`. A fixed-size buffer declarator introduces a new member and consists of an identifier that names the member, followed by a constant expression enclosed in `[` and `]` tokens. The constant expression denotes the number of elements in the member introduced by that fixed-size buffer declarator. The type of the constant expression must be implicitly convertible to type `int`, and the value must be a non-zero positive integer.

The elements of a fixed-size buffer shall be laid out sequentially in memory as though they are elements of an array.

A *field_declaration* with a *fixed_size_buffer_declarator* cannot have the `volatile` modifier, and it cannot be marked with a `System.ThreadStaticAttribute`.
A *field_declaration* with a *fixed_size_buffer_declarator* in an interface must have `static` modifier.

Depending on the situation (details are specified below), an access to a fixed-size buffer member is classified as a value (never a variable) of either
`System.ReadOnlySpan<S>` or `System.Span<S>`, where S is the element type of the fixed-size buffer. Both types provide indexers returning a reference to a
specific element with appropriate "readonly-ness", which prevents direct assignment to the elements when language rules don't permit that.

The resulting span instance will have a length equal to the size declared on the fixed-size buffer.
AlekseyTs marked this conversation as resolved.
Show resolved Hide resolved
Indexing into the span with a constant expression outside of the declared fixed-size buffer bounds is a compile time error.

The *safe-to-escape* scope of the value will be equal to the *safe-to-escape* scope of the container, just as it would if the backing data was accessed as a field.

### Fixed-size buffers in expressions
AlekseyTs marked this conversation as resolved.
Show resolved Hide resolved

Member lookup of a fixed-size buffer member proceeds exactly like member lookup of a field.

A fixed-size buffer can be referenced in an expression using a *simple_name* or a *member_access* .

When an instance fixed-size buffer member is referenced as a simple name, the effect is the same as a member access of the form `this.I`, where `I` is the fixed-size buffer member. When a static fixed-size buffer member is referenced as a simple name, the effect is the same as a member access of the form `E.I`, where `I` is the fixed-size buffer member and `E` is the declaring type.

#### Non-readonly fixed-size buffers

In a member access of the form `E.I`, if `E` is of a struct type and a member lookup of `I` in that struct type identifies an instance fixed-size member,
AlekseyTs marked this conversation as resolved.
Show resolved Hide resolved
then `E.I` is evaluated and classified as follows:

- If `E` is classified as a value, the result of the expression is classified as a value of type `System.ReadOnlySpan<S>`, where S is the element type of `I`.
The value can be used to access members’ elements.
AlekseyTs marked this conversation as resolved.
Show resolved Hide resolved
- Otherwise, `E` is classified as a variable and the result of the expression is classified as a value of type `System.Span<S>`, where S is the element type of `I`.
The value can be used to access members’ elements.
AlekseyTs marked this conversation as resolved.
Show resolved Hide resolved

In a member access of the form `E.I`, if `E` is of a class type and a member lookup of `I` in that class type identifies an instance fixed-size member,
then `E.I` is evaluated and classified as a value of type `System.Span<S>`, where S is the element type of `I`.

In a member access of the form `E.I`, if member lookup of `I` identifies a static fixed-size member,
then `E.I` is evaluated and classified as a value of type `System.Span<S>`, where S is the element type of `I`.

#### Readonly fixed-size buffers

When a *field_declaration* includes a `readonly` modifier, the member introduced by the fixed_size_buffer_declarator is a ***readony fixed-size buffer***.
AlekseyTs marked this conversation as resolved.
Show resolved Hide resolved
Direct assignments to elements of a readonly fixed-size buffer can only occur in an instance constructor, init member or static constructor in the same type.
Specifically, direct assignments to an element of readonly fixed-size buffer are permitted only in the following contexts:

- For an instance member, in the instance constructors or init member of the type that contains the member declaration; for a static member,
in the static constructor of the type that contains the member declaration. These are also the only contexts in which it is valid to pass
an element of readonly fixed-size buffer as an `out` or `ref` parameter.

Attempting to assign to an element of a readonly fixed-size buffer or pass it as an `out` or `ref` parameter in any other context is a compile-time error.
This is achieved by the following.

A member access for a readonly fixed-size buffer is evaluated and classified as follows:

- If access occurs in a context where direct assignments to an element of readonly fixed-size buffer are permitted, the result of the expression is classified as a value of type `System.Span<S>`, where S is the element type of the fixed-size buffer.
The value can be used to access members’ elements.
- Otherwise, the expression is classified as a value of type `System.ReadOnlySpan<S>`, where S is the element type of the fixed-size buffer.
The value can be used to access members’ elements.
AlekseyTs marked this conversation as resolved.
Show resolved Hide resolved

### Definite assignment checking

Fixed-size buffers are not subject to definite assignment-checking, and fixed-size buffer members are ignored for purposes of definite-assignment checking of struct type variables.

When a fixed-size buffer member is static or the outermost containing struct variable of a fixed-size buffer member is a static variable, an instance variable of a class instance, or an array element, the elements of the fixed-size buffer are automatically initialized to their default values. In all other cases, the initial content of a fixed-size buffer is undefined.
AlekseyTs marked this conversation as resolved.
Show resolved Hide resolved

## Open design questions

### Initializer

Should we support initialization at declaration site with, perhaps, [collection literals](https://github.com/dotnet/csharplang/blob/main/proposals/collection-literals.md)?

## Alternatives

### Method or property group like approach in the language
AlekseyTs marked this conversation as resolved.
Show resolved Hide resolved

One thought is to treat these members more like method groups, in that they aren't automatically a value in and of themselves,
but can be made into one if necessary. Here’s how that would work:
- Safe fixed-size buffer accesses have their own classification (just like e.g. method groups and lambdas)
- They can be indexed directly as a language operation (not via span types) to produce a variable (which is readonly
if the buffer is in a readonly context, just the same as fields of a struct)
- They have implicit conversions-from-expression to ```Span<T>``` and ```ReadOnlySpan<T>```, but use of the former is an error if
they are in a readonly context
- Their natural type is ```ReadOnlySpan<T>```, so that’s what they contribute if they participate in type inference (e.g., var, best-common-type or generic)

### C/C++ fixed-size bufers

C/C++ has a different notion of fixed-size bufers. For example, there is a notion of "zero-length fixed sized buffers",
which is often used as a way to indicate that the data is "variable length". It is not a goal of this proposal to be
able to interop with that.