diff --git a/standard/classes.md b/standard/classes.md index 06b1bc7f6..59d5f43c2 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -4453,8 +4453,12 @@ unary_operator_declarator : type 'operator' overloadable_unary_operator '(' fixed_parameter ')' ; +logical_negation_operator + : '!' + ; + overloadable_unary_operator - : '+' | '-' | '!' | '~' | '++' | '--' | 'true' | 'false' + : '+' | '-' | logical_negation_operator | '~' | '++' | '--' | 'true' | 'false' ; binary_operator_declarator @@ -4481,6 +4485,8 @@ operator_body *unsafe_modifier* ([§23.2](unsafe-code.md#232-unsafe-contexts)) is only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). +*Note*: The prefix logical negation ([§12.9.4](expressions.md#1294-logical-negation-operator)) and postfix null-forgiving operators ([§12.8.9](expressions.md#1289-null-forgiving-expressions)), while represented by the same lexical token (`!`), are distinct. The latter is not an overloadable operator. *end note* + There are three categories of overloadable operators: Unary operators ([§15.10.2](classes.md#15102-unary-operators)), binary operators ([§15.10.3](classes.md#15103-binary-operators)), and conversion operators ([§15.10.4](classes.md#15104-conversion-operators)). The *operator_body* is either a semicolon, a block body ([§15.6.1](classes.md#1561-general)) or an expression body ([§15.6.1](classes.md#1561-general)). A block body consists of a *block*, which specifies the statements to execute when the operator is invoked. The *block* shall conform to the rules for value-returning methods described in [§15.6.11](classes.md#15611-method-body). An expression body consists of `=>` followed by an expression and a semicolon, and denotes a single expression to perform when the operator is invoked. @@ -4507,7 +4513,7 @@ Additional information on conversion operators can be found in [§10.5](convers The following rules apply to unary operator declarations, where `T` denotes the instance type of the class or struct that contains the operator declaration: -- A unary `+`, `-`, `!`, or `~` operator shall take a single parameter of type `T` or `T?` and can return any type. +- A unary `+`, `-`, `!` (logical negation only), or `~` operator shall take a single parameter of type `T` or `T?` and can return any type. - A unary `++` or `--` operator shall take a single parameter of type `T` or `T?` and shall return that same type or a type derived from it. - A unary `true` or `false` operator shall take a single parameter of type `T` or `T?` and shall return type `bool`. diff --git a/standard/expressions.md b/standard/expressions.md index 2b15144b3..aa24fc0b3 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -59,7 +59,7 @@ The following operations in C# are subject to binding: - Delegate invocation: `e(e₁,...,eᵥ)` - Element access: `e[e₁,...,eᵥ]` - Object creation: new `C(e₁,...,eᵥ)` -- Overloaded unary operators: `+`, `-`, `!`, `~`, `++`, `--`, `true`, `false` +- Overloaded unary operators: `+`, `-`, `!` (logical negation only), `~`, `++`, `--`, `true`, `false` - Overloaded binary operators: `+`, `-`, `*`, `/`, `%`, `&`, `&&`, `|`, `||`, `??`, `^`, `<<`, `>>`, `==`, `!=`, `>`, `<`, `>=`, `<=` - Assignment operators: `=`, `= ref`, `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `<<=`, `>>=` - Implicit and explicit conversions @@ -147,8 +147,8 @@ The precedence of an operator is established by the definition of its associated > > | **Subclause** | **Category** | **Operators** | > | ----------------- | ------------------------------- | -------------------------------------------------------| -> | [§12.8](expressions.md#128-primary-expressions) | Primary | `x.y` `x?.y` `f(x)` `a[x]` `a?[x]` `x++` `x--` `new` `typeof` `default` `checked` `unchecked` `delegate` `stackalloc` | -> | [§12.9](expressions.md#129-unary-operators) | Unary | `+` `-` `!` `~` `++x` `--x` `(T)x` `await x` | +> | [§12.8](expressions.md#128-primary-expressions) | Primary | `x.y` `x?.y` `f(x)` `a[x]` `a?[x]` `x++` `x--` `x!` `new` `typeof` `default` `checked` `unchecked` `delegate` `stackalloc` | +> | [§12.9](expressions.md#129-unary-operators) | Unary | `+` `-` `!x` `~` `++x` `--x` `(T)x` `await x` | > | [§12.10](expressions.md#1210-arithmetic-operators) | Multiplicative | `*` `/` `%` | > | [§12.10](expressions.md#1210-arithmetic-operators) | Additive | `+` `-` | > | [§12.11](expressions.md#1211-shift-operators) | Shift | `<<` `>>` | @@ -182,11 +182,11 @@ All unary and binary operators have predefined implementations. In addition, use The ***overloadable unary operators*** are: -```csharp -+ - ! ~ ++ -- true false -``` +`+ - !` (logical negation only) `~ ++ -- true false` > *Note*: Although `true` and `false` are not used explicitly in expressions (and therefore are not included in the precedence table in [§12.4.2](expressions.md#1242-operator-precedence-and-associativity)), they are considered operators because they are invoked in several expression contexts: Boolean expressions ([§12.24](expressions.md#1224-boolean-expressions)) and expressions involving the conditional ([§12.18](expressions.md#1218-conditional-operator)) and conditional logical operators ([§12.14](expressions.md#1214-conditional-logical-operators)). *end note* +> +> *Note*: The null-forgiving operator (postfix `!`, [§12.8.9](expressions.md#1289-null-forgiving-expressions)) is not an overloadable operator. *end note* The ***overloadable binary operators*** are: @@ -295,7 +295,7 @@ When overload resolution rules ([§12.6.4](expressions.md#1264-overload-resoluti **This subclause is informative.** -Unary numeric promotion occurs for the operands of the predefined `+`, `–`, and `~` unary operators. Unary numeric promotion simply consists of converting operands of type `sbyte`, `byte`, `short`, `ushort`, or `char` to type `int`. Additionally, for the unary – operator, unary numeric promotion converts operands of type `uint` to type `long`. +Unary numeric promotion occurs for the operands of the predefined `+`, `-`, and `~` unary operators. Unary numeric promotion simply consists of converting operands of type `sbyte`, `byte`, `short`, `ushort`, or `char` to type `int`. Additionally, for the unary – operator, unary numeric promotion converts operands of type `uint` to type `long`. **End of informative text.** @@ -303,7 +303,7 @@ Unary numeric promotion occurs for the operands of the predefined `+`, `–`, an **This subclause is informative.** -Binary numeric promotion occurs for the operands of the predefined `+`, `–`, `*`, `/`, `%`, `&`, `|`, `^`, `==`, `!=`, `>`, `<`, `>=`, and `<=` binary operators. Binary numeric promotion implicitly converts both operands to a common type which, in case of the non-relational operators, also becomes the result type of the operation. Binary numeric promotion consists of applying the following rules, in the order they appear here: +Binary numeric promotion occurs for the operands of the predefined `+`, `-`, `*`, `/`, `%`, `&`, `|`, `^`, `==`, `!=`, `>`, `<`, `>=`, and `<=` binary operators. Binary numeric promotion implicitly converts both operands to a common type which, in case of the non-relational operators, also becomes the result type of the operation. Binary numeric promotion consists of applying the following rules, in the order they appear here: - If either operand is of type `decimal`, the other operand is converted to type `decimal`, or a binding-time error occurs if the other operand is of type `float` or `double`. - Otherwise, if either operand is of type `double`, the other operand is converted to type `double`. @@ -346,7 +346,7 @@ In both of the above cases, a cast expression can be used to explicitly convert ***Lifted operators*** permit predefined and user-defined operators that operate on non-nullable value types to also be used with nullable forms of those types. Lifted operators are constructed from predefined and user-defined operators that meet certain requirements, as described in the following: -- For the unary operators `+`, `++`, `-`, `--`, `!`, and `~`, a lifted form of an operator exists if the operand and result types are both non-nullable value types. The lifted form is constructed by adding a single `?` modifier to the operand and result types. The lifted operator produces a `null` value if the operand is `null`. Otherwise, the lifted operator unwraps the operand, applies the underlying operator, and wraps the result. +- For the unary operators `+`, `++`, `-`, `--`, `!`(logical negation), and `~`, a lifted form of an operator exists if the operand and result types are both non-nullable value types. The lifted form is constructed by adding a single `?` modifier to the operand and result types. The lifted operator produces a `null` value if the operand is `null`. Otherwise, the lifted operator unwraps the operand, applies the underlying operator, and wraps the result. - For the binary operators `+`, `-`, `*`, `/`, `%`, `&`, `|`, `^`, `<<`, and `>>`, a lifted form of an operator exists if the operand and result types are all non-nullable value types. The lifted form is constructed by adding a single `?` modifier to each operand and result type. The lifted operator produces a `null` value if one or both operands are `null` (an exception being the `&` and `|` operators of the `bool?` type, as described in [§12.13.5](expressions.md#12135-nullable-boolean--and--operators)). Otherwise, the lifted operator unwraps the operands, applies the underlying operator, and wraps the result. - For the equality operators `==` and `!=`, a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is `bool`. The lifted form is constructed by adding a single `?` modifier to each operand type. The lifted operator considers two `null` values equal, and a `null` value unequal to any non-`null` value. If both operands are non-`null`, the lifted operator unwraps the operands and applies the underlying operator to produce the `bool` result. - For the relational operators `<`, `>`, `<=`, and `>=`, a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is `bool`. The lifted form is constructed by adding a single `?` modifier to each operand type. The lifted operator produces the value `false` if one or both operands are `null`. Otherwise, the lifted operator unwraps the operands and applies the underlying operator to produce the `bool` result. @@ -1273,7 +1273,6 @@ Primary expressions include the simplest forms of expressions. primary_expression : primary_no_array_creation_expression | array_creation_expression - | null_forgiving_expression ; primary_no_array_creation_expression @@ -1291,6 +1290,7 @@ primary_no_array_creation_expression | base_access | post_increment_expression | post_decrement_expression + | null_forgiving_expression | object_creation_expression | delegate_creation_expression | anonymous_object_creation_expression @@ -1307,7 +1307,7 @@ primary_no_array_creation_expression ; ``` -> *Note*: These grammar rules are not ANTLR-ready as they are part of a set of mutually left-recursive rules (`primary_expression`, `primary_no_array_creation_expression`, `member_access`, `invocation_expression`, `element_access`, `post_increment_expression`, `post_decrement_expression`, `pointer_member_access` and `pointer_element_access`) which ANTLR does not handle. Standard techniques can be used to transform the grammar to remove the mutual left-recursion. This has not been done as not all parsing strategies require it (e.g. an LALR parser would not) and doing so would obfuscate the structure and description. *end note* +> *Note*: These grammar rules are not ANTLR-ready as they are part of a set of mutually left-recursive rules (`primary_expression`, `primary_no_array_creation_expression`, `member_access`, `invocation_expression`, `element_access`, `post_increment_expression`, `post_decrement_expression`, `null_forgiving_expression`, `pointer_member_access` and `pointer_element_access`) which ANTLR does not handle. Standard techniques can be used to transform the grammar to remove the mutual left-recursion. This has not been done as not all parsing strategies require it (e.g. an LALR parser would not) and doing so would obfuscate the structure and description. *end note* *pointer_member_access* ([§23.6.3](unsafe-code.md#2363-pointer-member-access)) and *pointer_element_access* ([§23.6.4](unsafe-code.md#2364-pointer-element-access)) are only available in unsafe code ([§23](unsafe-code.md#23-unsafe-code)). @@ -1749,12 +1749,12 @@ In a member access of the form `E.I`, if `E` is a single identifier, and if the A *null_conditional_member_access* is a conditional version of *member_access* ([§12.8.7](expressions.md#1287-member-access)) and it is a binding time error if the result type is `void`. For a null conditional expression where the result type may be `void` see ([§12.8.11](expressions.md#12811-null-conditional-invocation-expression)). -A *null_conditional_member_access* consists of a *primary_expression* followed by the two tokens “`?`” and “`.`”, followed by an *identifier* with an optional *type_argument_list*, followed by zero or more *dependent_access*es. +A *null_conditional_member_access* consists of a *primary_expression* followed by the two tokens “`?`” and “`.`”, followed by an *identifier* with an optional *type_argument_list*, followed by zero or more *dependent_access*es any of which can be preceeded by a *null_forgiving_operator*. ```ANTLR null_conditional_member_access : primary_expression '?' '.' identifier type_argument_list? - dependent_access* + (null_forgiving_operator? dependent_access)* ; dependent_access @@ -1824,21 +1824,39 @@ A *null_conditional_projection_initializer* is a restriction of *null_conditiona ### 12.8.9 Null-forgiving expressions -This operator sets the null state ([§8.9.5](types.md#895-nullabilities-and-null-states)) of the operand to “not null”. +A null-forgiving expression’s value, type, classification ([§12.2](expressions.md#122-expression-classifications)) +and safe-context ([§16.4.12](structs.md#16412-safe-context-constraint)) is the value, type, classification and safe-context of its *primary_expression*. ```ANTLR null_forgiving_expression - : primary_no_array_creation_expression suppression + : primary_expression null_forgiving_operator ; -suppression +null_forgiving_operator : '!' ; ``` -This operator has no runtime effect; it evaluates to the result of its operand, and that result retains that operand’s classification. +*Note*: The postfix null-forgiving and prefix logical negation operators ([§12.9.4](expressions.md#1294-logical-negation-operator)), while represented by the same lexical token (`!`), are distinct. Only the latter may be overriden ([§15.10](classes.md#1510-operators)), the definition of the null-forgiving operator is fixed. *end note* + + + +**The remainder of this subclause, including all of its subclauses, is conditionally normative.** + +A compiler which performs static null state analysis ([§8.9.5](types.md#895-nullabilities-and-null-states)) must conform to the following specification. + +The null-forgiving operator is a compile time pseudo-operation that is used to inform a compiler’s static null state analysis. It has two uses: to override a compiler’s determination that an expression *maybe null*; and to override a compiler issuing a warning related to nullability. + +Applying the null-forgiving operator to an expression for which a compiler’s static null state analysis +does not produce any warnings is not an error. + +#### 12.8.9.1 Overriding a *maybe null* determination -The null-forgiving operator is used to declare that an expression not known to be a value type is not null. +Under some circumstances a compiler’s static null state analysis may determine that an expression +has the null state *maybe null* and issue a warning when other information indicates that the +expression cannot be null. Applying the null-forgiving operator to such an expression informs the +compiler’s static null state analysis that the null state is in *not null*; which both prevents the warning +message and informs any ongoing analysis. > *Example*: Consider the following: > @@ -1859,9 +1877,60 @@ The null-forgiving operator is used to declare that an expression not known to b > person != null && person.Name != null; > ``` > -> If `IsValid` returns `true`, `p` can safely be dereferenced to access its `Name` property, and the “dereferencing of a possibly null value” warning can be suppressed using `!`. *end example* +> If `IsValid` returns `true`, `p` can safely be dereferenced to access its `Name` property, and the “dereferencing of a possibly null value” warning can be suppressed using `!`. +> +> *end example* +> +> *Example:* The null-forgiving operator should be used with caution, consider: +> +> ```csharp +> #nullable enable +> int B(int? x) +> { +> int y = (int)x!; // quash warning, throw at runtime if x is null +> return y; +> } +> ``` +> +> Here the null-forgiving operator is applied to a value type and quashes any warning on +> `x`. However if `x` is `null` at runtime an exception will be thrown as `null` cannot +> be cast to `int`. +> +> *end example* + +#### 12.8.9.2 Overriding other null analysis warnings -The null state ([§8.9.5](types.md#895-nullabilities-and-null-states)) of a *null_forgiving_expression* is “not null.” +In addition to overriding *maybe null* determinations as above there may be other circumstances +where it is desired to override a compiler’s static null state analysis determination that an +expression requires one or more warnings. Applying the +null-forgiving operator to such an expression requests that the compiler +does not issue any warnings for the expression. In response a compiler may choose not +to issue warnings and may also modify its further analysis. + +> *Example*: Consider the following: +> +> ```csharp +> #nullable enable +> public static void Assign(out string? lv, string? rv) { lv = rv; } +> +> public string M(string? t) +> { +> string s; +> Assign(out s!, t ?? "«argument was null»"); +> return s; +> } +> ``` +> +> The types of method `Assign`’s parameters, `lv` & `rv`, are `string?`, with `lv` being +> an output parameter, and it performs a simple assignment. +> +> Method `M` passes the variable `s`, of type `string`, as `Assign`’s output parameter, the +> compiler used issues a warning as `s` is not a nullable variable. Given that `Assign`’s +> second argument cannot be null the null-forgiving operator is used to quash the warning. +> +> *end example* + +**End of conditionally normative text.** ### 12.8.10 Invocation expressions @@ -1875,6 +1944,8 @@ invocation_expression ; ``` + + An *invocation_expression* is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)) if at least one of the following holds: - The *primary_expression* has compile-time type `dynamic`. @@ -2073,11 +2144,13 @@ Unlike the syntactically equivalent *null_conditional_member_access* or *null_co ```ANTLR null_conditional_invocation_expression - : null_conditional_member_access '(' argument_list? ')' - | null_conditional_element_access '(' argument_list? ')' + : null_conditional_member_access null_forgiving_operator? '(' argument_list? ')' + | null_conditional_element_access null_forgiving_operator? '(' argument_list? ')' ; ``` + + A *null_conditional_invocation_expression* expression `E` is of the form `P?A`; where `A` is the remainder of the syntactically equivalent *null_conditional_member_access* or *null_conditional_element_access*, `A` will therefore start with `.` or `[`. Let `PA` signify the concatention of `P` and `A`. When `E` occurs as a *statement_expression* the meaning of `E` is the same as the meaning of the *statement*: @@ -2161,12 +2234,12 @@ Depending on the context in which it is used, an indexer access causes invocatio ### 12.8.13 Null Conditional Element Access -A *null_conditional_element_access* consists of a *primary_no_array_creation_expression* followed by the two tokens “`?`” and “`[`”, followed by an *argument_list*, followed by a “`]`” token, followed by zero or more *dependent_access*es. +A *null_conditional_element_access* consists of a *primary_no_array_creation_expression* followed by the two tokens “`?`” and “`[`”, followed by an *argument_list*, followed by a “`]`” token, followed by zero or more *dependent_access*es any of which can be preceeded by a *null_forgiving_operator*. ```ANTLR null_conditional_element_access : primary_no_array_creation_expression '?' '[' argument_list ']' - dependent_access* + (null_forgiving_operator? dependent_access)* ; ``` @@ -3360,14 +3433,16 @@ An *anonymous_method_expression* is one of two ways of defining an anonymous fun ### 12.9.1 General -The `+`, `-`, `!`, `~`, `++`, `--`, cast, and `await` operators are called the unary operators. +The `+`, `-`, `!` (logical negation [§12.9.4](expressions.md#1294-logical-negation-operator) only), `~`, `++`, `--`, cast, and `await` operators are called the unary operators. + +> *Note*: The postfix null-forgiving operator ([§12.8.9](expressions.md#1289-null-forgiving-expressions)), `!`, due to its compile-time and non-overloadable only nature, is excluded from the above list. *end note* ```ANTLR unary_expression : primary_expression | '+' unary_expression | '-' unary_expression - | '!' unary_expression + | logical_negation_operator unary_expression | '~' unary_expression | pre_increment_expression | pre_decrement_expression @@ -3446,6 +3521,8 @@ This operator computes the logical negation of the operand: If the operand is `t Lifted ([§12.4.8](expressions.md#1248-lifted-operators)) forms of the unlifted predefined logical negation operator defined above are also predefined. +*Note*: The prefix logical negation and postfix null-forgiving operators ([§12.8.9](expressions.md#1289-null-forgiving-expressions)), while represented by the same lexical token (`!`), are distinct. *end note* + ### 12.9.5 Bitwise complement operator For an operation of the form `~x`, unary operator overload resolution ([§12.4.4](expressions.md#1244-unary-operator-overload-resolution)) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. The predefined bitwise complement operators are: @@ -3605,7 +3682,7 @@ An awaiter’s implementation of the interface methods `INotifyCompletion.OnComp ### 12.10.1 General -The `*`, `/`, `%`, `+`, and `–` operators are called the arithmetic operators. +The `*`, `/`, `%`, `+`, and `-` operators are called the arithmetic operators. ```ANTLR multiplicative_expression @@ -4412,9 +4489,9 @@ The tuple equality operator `x == y` is evaluated as follows: - For each pair of elements `xi` and `yi` in lexical order: - The operator `xi == yi` is evaluated, and a result of type `bool` is obtained in the following way: - If the comparison yielded a `bool` then that is the result. - - Otherwise if the comparison yielded a `dynamic` then the operator `false` is dynamically invoked on it, and the resulting `bool` value is negated with the `!` operator. + - Otherwise if the comparison yielded a `dynamic` then the operator `false` is dynamically invoked on it, and the resulting `bool` value is negated with the logical negation operator (`!`). - Otherwise, if the type of the comparison has an implicit conversion to `bool`, that conversion is applied. - - Otherwise, if the type of the comparison has an operator `false`, that operator is invoked and the resulting `bool` value is negated with the `!` operator. + - Otherwise, if the type of the comparison has an operator `false`, that operator is invoked and the resulting `bool` value is negated with the logical negation operator (`!`). - If the resulting `bool` is `false`, then no further evaluation occurs, and the result of the tuple equality operator is `false`. - If all element comparisons yielded `true`, the result of the tuple equality operator is `true`. @@ -6377,6 +6454,8 @@ assignment_operator ; ``` + + The left operand of an assignment shall be an expression classified as a variable, or, except for `= ref`, a property access, an indexer access, an event access or a tuple. A declaration expression is not directly permitted as a left operand, but may occur as a step in the evaluation of a deconstructing assignment. The `=` operator is called the ***simple assignment operator***. It assigns the value or values of the right operand to the variable, property, indexer element or tuple elements given by the left operand. The left operand of the simple assignment operator shall not be an event access (except as described in [§15.8.2](classes.md#1582-field-like-events)). The simple assignment operator is described in [§12.21.2](expressions.md#12212-simple-assignment). @@ -6672,9 +6751,10 @@ Only the following constructs are permitted in constant expressions: - Cast expressions. - `checked` and `unchecked` expressions. - `nameof` expressions. -- The predefined `+`, `–`, `!`, and `~` unary operators. -- The predefined `+`, `–`, `*`, `/`, `%`, `<<`, `>>`, `&`, `|`, `^`, `&&`, `||`, `==`, `!=`, `<`, `>`, `<=`, and `>=` binary operators. +- The predefined `+`, `-`, `!` (logical negation) and `~` unary operators. +- The predefined `+`, `-`, `*`, `/`, `%`, `<<`, `>>`, `&`, `|`, `^`, `&&`, `||`, `==`, `!=`, `<`, `>`, `<=`, and `>=` binary operators. - The `?:` conditional operator. +- The `!` null-forgiving operator ([§12.9.4](expressions.md#1289-null-forgiving-expressions)). - `sizeof` expressions, provided the unmanaged-type is one of the types specified in [§23.6.9](unsafe-code.md#2369-the-sizeof-operator) for which `sizeof` returns a constant value. - Default value expressions, provided the type is one of the types listed above. diff --git a/standard/lexical-structure.md b/standard/lexical-structure.md index fccf58e02..79d336bbd 100644 --- a/standard/lexical-structure.md +++ b/standard/lexical-structure.md @@ -1149,7 +1149,7 @@ The namespace for conditional compilation symbols is distinct and separate from ### 6.5.3 Pre-processing expressions -Pre-processing expressions can occur in `#if` and `#elif` directives. The operators `!`, `==`, `!=`, `&&`, and `||` are permitted in pre-processing expressions, and parentheses may be used for grouping. +Pre-processing expressions can occur in `#if` and `#elif` directives. The operators `!` (prefix logical negation only), `==`, `!=`, `&&`, and `||` are permitted in pre-processing expressions, and parentheses may be used for grouping. ```ANTLR fragment PP_Expression diff --git a/standard/types.md b/standard/types.md index 806589bb8..25f8449b8 100644 --- a/standard/types.md +++ b/standard/types.md @@ -787,7 +787,7 @@ When the nullable context is ***annotations***: - For any reference type `T`, the annotation `?` in `T?` indicates that `T?` a nullable type, whereas the unannotated `T` is non-nullable. - No diagnostic warnings related to nullability are generated. -- The null-forgiving operator `!` ([§12.8.9](expressions.md#1289-null-forgiving-expressions)) sets the null state of its operand to *not null*. +- The null-forgiving operator `!` ([§12.8.9](expressions.md#1289-null-forgiving-expressions)) may alter the analyzed null state of its operand and what compile time informative messages are produced. > *Example*: > diff --git a/standard/unsafe-code.md b/standard/unsafe-code.md index b18ec8b51..82214fdd0 100644 --- a/standard/unsafe-code.md +++ b/standard/unsafe-code.md @@ -544,6 +544,8 @@ addressof_expression ; ``` + + Given an expression `E` which is of a type `T` and is classified as a fixed variable ([§23.4](unsafe-code.md#234-fixed-and-moveable-variables)), the construct `&E` computes the address of the variable given by `E`. The type of the result is `T*` and is classified as a value. A compile-time error occurs if `E` is not classified as a variable, if `E` is classified as a read-only local variable, or if `E` denotes a moveable variable. In the last case, a fixed statement ([§23.7](unsafe-code.md#237-the-fixed-statement)) can be used to temporarily “fix” the variable before obtaining its address. > *Note*: As stated in [§12.8.7](expressions.md#1287-member-access), outside an instance constructor or static constructor for a struct or class that defines a `readonly` field, that field is considered a value, not a variable. As such, its address cannot be taken. Similarly, the address of a constant cannot be taken. @@ -596,7 +598,7 @@ If a pointer increment or decrement operation overflows the domain of the pointe ### 23.6.7 Pointer arithmetic -In an unsafe context, the `+` operator ([§12.10.5](expressions.md#12105-addition-operator)) and `–` operator ([§12.10.6](expressions.md#12106-subtraction-operator)) can be applied to values of all pointer types except `void*`. Thus, for every pointer type `T*`, the following operators are implicitly defined: +In an unsafe context, the `+` operator ([§12.10.5](expressions.md#12105-addition-operator)) and `-` operator ([§12.10.6](expressions.md#12106-subtraction-operator)) can be applied to values of all pointer types except `void*`. Thus, for every pointer type `T*`, the following operators are implicitly defined: ```csharp T* operator +(T* x, int y);