Skip to content

Commit 3322142

Browse files
committed
Add additional examples.
Add three more examples to illustrate the ref safety rules for scoped ref. These highlight the potential issues with an unscoped ref.
1 parent 8e1286e commit 3322142

2 files changed

Lines changed: 82 additions & 0 deletions

File tree

standard/structs.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,37 @@ The safe-context of an object initializer expression is the narrowest of:
11521152
11531153
> *Note*: Another way of modeling this is to consider any argument to a member initializer that can be assigned to the receiver as being an argument to the constructor. *end note*
11541154
1155+
> *Example*: The following illustrates how an object initializer narrows the safe-context of the resulting value:
1156+
>
1157+
> <!-- Example: {template:"standalone-lib-without-using", name:"ObjectInitializerSafeContext", expectedErrors:["CS8352"]} -->
1158+
> ```csharp
1159+
> using System;
1160+
>
1161+
> ref struct S
1162+
> {
1163+
> public Span<int> Field;
1164+
> public S(ref int i) { }
1165+
> }
1166+
>
1167+
> class C
1168+
> {
1169+
> static S Example()
1170+
> {
1171+
> Span<int> stackSpan = stackalloc int[42];
1172+
> int i = 0;
1173+
>
1174+
> // safe-context is narrowest of:
1175+
> // constructor safe-context (caller-context) and
1176+
> // RHS of Field assignment (stackSpan: function-member)
1177+
> // = function-member
1178+
> var x = new S(ref i) { Field = stackSpan };
1179+
> return x; // Error: x has safe-context of function-member
1180+
> }
1181+
> }
1182+
> ```
1183+
>
1184+
> *end example*
1185+
11551186
#### 16.5.15.7 stackalloc
11561187
11571188
The result of a stackalloc expression has safe-context of function-member.

standard/variables.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1368,6 +1368,31 @@ For a parameter `p`:
13681368
13691369
When a parameter is annotated with `[UnscopedRef]` ([§UnscopedRefAttribute](attributes.md#unscopedrefattribute-the-unscopedref-attribute)), its ref-safe-context is widened by one level from its default: function-member becomes return-only, and return-only becomes caller-context.
13701370
1371+
> *Example*: The following illustrates how the implicit `this` parameter of a struct instance method is `scoped ref` (ref-safe-context of *function-member*), and how `[UnscopedRef]` widens it to *return-only*, enabling ref returns of fields:
1372+
>
1373+
> <!-- Example: {template:"standalone-lib-without-using", name:"ParameterRefSafeContext", expectedErrors:["CS8170"]} -->
1374+
> ```csharp
1375+
> using System.Diagnostics.CodeAnalysis;
1376+
>
1377+
> struct S
1378+
> {
1379+
> private int _field;
1380+
>
1381+
> // Error: ref-safe-context of `this` is function-member,
1382+
> // so ref-safe-context of `_field` is also function-member,
1383+
> // which does not satisfy the return-only requirement.
1384+
> public ref int Bad() => ref _field;
1385+
>
1386+
> // OK: [UnscopedRef] widens `this` from function-member to
1387+
> // return-only, so `_field` also has ref-safe-context of
1388+
> // return-only, satisfying the ref return requirement.
1389+
> [UnscopedRef]
1390+
> public ref int Good() => ref _field;
1391+
> }
1392+
> ```
1393+
>
1394+
> *end example*
1395+
13711396
#### 9.7.2.4 Field ref safe context
13721397
13731398
For a variable designating a reference to a field, `e.F`:
@@ -1485,3 +1510,29 @@ Any other difference with respect to `scoped` or `[UnscopedRef]` between the bas
14851510
The `scoped` modifier and `[UnscopedRef]` attribute do not affect hiding.
14861511
14871512
Overloads shall not differ only on `scoped` or `[UnscopedRef]`.
1513+
1514+
> *Example*: The following illustrates valid and invalid scope variance in overrides:
1515+
>
1516+
> <!-- Example: {template:"standalone-lib-without-using", name:"ParameterScopeVariance", expectedErrors:["CS0111"]} -->
1517+
> ```csharp
1518+
> using System;
1519+
>
1520+
> class Base
1521+
> {
1522+
> public virtual void M(ref Span<int> x) { }
1523+
> }
1524+
>
1525+
> class Derived : Base
1526+
> {
1527+
> // OK: adds scoped to a ref parameter
1528+
> public override void M(scoped ref Span<int> x) { }
1529+
> }
1530+
>
1531+
> class C
1532+
> {
1533+
> void N(Span<int> x) { }
1534+
> void N(scoped Span<int> x) { } // Error: overloads differ only on scoped
1535+
> }
1536+
> ```
1537+
>
1538+
> *end example*

0 commit comments

Comments
 (0)