diff --git a/standard/classes.md b/standard/classes.md index 24b500c65..88eaff42d 100644 --- a/standard/classes.md +++ b/standard/classes.md @@ -233,7 +233,7 @@ The base class specified in a class declaration can be a constructed class type The direct base class of a class type shall be at least as accessible as the class type itself ([§7.5.5](basic-concepts.md#755-accessibility-constraints)). For example, it is a compile-time error for a public class to derive from a private or internal class. -The direct base class of a class type shall not be any of the following types: `System.Array`, `System.Delegate`, `System.Enum`, or `System.ValueType`. Furthermore, a generic class declaration shall not use `System.Attribute` as a direct or indirect base class ([§22.2.1](attributes.md#2221-general)). +The direct base class of a class type shall not be any of the following types: `System.Array`, `System.Delegate`, `System.Enum`, `System.ValueType` or the `dynamic` type. Furthermore, a generic class declaration shall not use `System.Attribute` as a direct or indirect base class ([§22.2.1](attributes.md#2221-general)). In determining the meaning of the direct base class specification `A` of a class `B`, the direct base class of `B` is temporarily assumed to be `object`, which ensures that the meaning of a base class specification cannot recursively depend on itself. @@ -2859,7 +2859,7 @@ Partial methods shall not define access modifiers; they are implicitly private. There are two kinds of partial method declarations: If the body of the method declaration is a semicolon, the declaration is said to be a ***defining partial method declaration***. If the body is other than a semicolon, the declaration is said to be an ***implementing partial method declaration***. Across the parts of a type declaration, there may be only one defining partial method declaration with a given signature, and there may be only one implementing partial method declaration with a given signature. If an implementing partial method declaration is given, a corresponding defining partial method declaration shall exist, and the declarations shall match as specified in the following: - The declarations shall have the same modifiers (although not necessarily in the same order), method name, number of type parameters and number of parameters. -- Corresponding parameters in the declarations shall have the same modifiers (although not necessarily in the same order) and the same types (modulo differences in type parameter names). +- Corresponding parameters in the declarations shall have the same modifiers (although not necessarily in the same order) and the same types, or identity convertible types (modulo differences in type parameter names). - Corresponding type parameters in the declarations shall have the same constraints (modulo differences in type parameter names). An implementing partial method declaration can appear in the same part as the corresponding defining partial method declaration. diff --git a/standard/conversions.md b/standard/conversions.md index 0ed9932f7..010b472d3 100644 --- a/standard/conversions.md +++ b/standard/conversions.md @@ -71,19 +71,47 @@ The pre-defined implicit conversions always succeed and never cause exceptions t > *Note*: Properly designed user-defined implicit conversions should exhibit these characteristics as well. *end note* -For the purposes of conversion, the types `object` and `dynamic` are considered equivalent. +For the purposes of conversion, the types `object` and `dynamic` are identity convertible (§10.2.2). However, dynamic conversions ([§10.2.10](conversions.md#10210-implicit-dynamic-conversions) and [§10.3.8](conversions.md#1038-explicit-dynamic-conversions)) apply only to expressions of type `dynamic` ([§8.2.4](types.md#824-the-dynamic-type)). ### 10.2.2 Identity conversion -An identity conversion converts from any type to the same type. One reason this conversion exists is so that a type T or an expression of type T can be said to be convertible to T itself. +An identity conversion converts from any type to the same type or a type that is equivalent at runtime. One reason this conversion exists is so that a type `T` or an expression of type `T` can be said to be convertible to `T` itself. The following identity conversions exist: -In some cases there is an identity conversion between types that are not exactly the same, but are considered equivalent. Such identity conversions exist: +- Between `T` and `T`, for any type `T`. +- Between `object` and `dynamic`. +- Between all tuple types with the same arity, and the corresponding constructed `ValueTuple<...>` type, when an identity conversion exists between each pair of corresponding element types. +- Between types constructed from the same generic type where there exists an identity conversion between each corresponding type argument. -- between `object` and `dynamic`. -- between tuple types with the same arity, when an identity conversion exists between each pair of corresponding element types. -- between types constructed from the same generic type where there exists an identity conversion between each corresponding type argument. +> *Example*: The following illustrates the recursive nature of the third rule: +> +> +> ```csharp +> (int a , string b) t1 = (1, "two"); +> (int c, string d) t2 = (3, "four"); +> +> // Identity conversions exist between +> // the types of t1, t2, and t3. +> var t3 = (5, "six"); +> t3 = t2; +> t2 = t1; +> +> var t4 = (t1, 7); +> var t5 = (t2, 8); +> +> // Identity conversions exist between +> // the types of t4, t5, and t6. +> var t6 =((8, "eight"), 9); +> t6 = t5; +> t5 = t4; +> ``` +> +> The types of tuples `t1`, `t2` and `t3` all have two elements: an `int` followed by a `string`. Tuple element types may themselves by tuples, as in `t4`, `t5`, and `t6`. An identity conversion exists between each pair of corresponding element types, including nested tuples, therefore an identity conversion exists between the types of tuples `t4`, `t5`, and `t6`. +> +> *end example* + +All identity conversions are symmetric. If an identity conversion exists from `T₁` to `T₂`, then an identity conversion exists from `T₂` to `T₁`. Two types are *identity convertible* when an identity conversion exists between two types. In most cases, an identity conversion has no effect at runtime. However, since floating point operations may be performed at higher precision than prescribed by their type ([§8.3.7](types.md#837-floating-point-types)), assignment of their results may result in a loss of precision, and explicit casts are guaranteed to reduce precision to what is prescribed by the type ([§12.9.7](expressions.md#1297-cast-expressions)). @@ -374,7 +402,7 @@ Explicit conversions can occur in cast expressions ([§12.9.7](expressions.md#12 The set of explicit conversions includes all implicit conversions. -> *Note*: This, for example, allows an explicit cast to be used when an implicit conversion to the same type exists, in order to force the selection of a particular method overload. *end note* +> *Note*: This, for example, allows an explicit cast to be used when an implicit identity conversion exists, in order to force the selection of a particular method overload. *end note* The explicit conversions that are not implicit conversions are conversions that cannot be proven always to succeed, conversions that are known possibly to lose information, and conversions across domains of types sufficiently different to merit explicit notation. @@ -772,7 +800,7 @@ Specifically, an anonymous function `F` is compatible with a delegate type `D` - If `F` contains an *anonymous_function_signature*, then `D` and `F` have the same number of parameters. - 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 explicitly typed parameter list, each parameter in `D` has the same modifiers as the corresponding parameter in `F` and an identity conversion exists between 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 a `«TaskType»` return type ([§15.15.1](classes.md#15151-general)), then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid expression (w.r.t [§12](expressions.md#12-expressions)) that would be permitted as a *statement_expression* ([§13.7](statements.md#137-expression-statements)). - If the body of `F` is a block, and *either* `D` has a void return type *or* `F` is async and `D` has a `«TaskType»` return type , then when each parameter of `F` is given the type of the corresponding parameter in `D`, the body of `F` is a valid block (w.r.t [§13.3](statements.md#133-blocks)) in which no `return` statement specifies an expression. diff --git a/standard/expressions.md b/standard/expressions.md index 0a1302e61..f3c3d93f1 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -245,7 +245,7 @@ An operation of the form `«op» x` or `x «op»`, where «op» is an overloada An operation of the form `x «op» y`, where «op» is an overloadable binary operator, `x` is an expression of type `X`, and `y` is an expression of type `Y`, is processed as follows: - The set of candidate user-defined operators provided by `X` and `Y` for the operation `operator «op»(x, y)` is determined. The set consists of the union of the candidate operators provided by `X` and the candidate operators provided by `Y`, each determined using the rules of [§12.4.6](expressions.md#1246-candidate-user-defined-operators). For the combined set, candidates are merged as follows: - - If `X` and `Y` are the same type, or if `X` and `Y` are derived from a common base type, then shared candidate operators only occur in the combined set once. + - If `X` and `Y` are identity convertible, or if `X` and `Y` are derived from a common base type, then shared candidate operators only occur in the combined set once. - If there is an identity conversion between `X` and `Y`, an operator `«op»Y` provided by `Y` has the same return type as an `«op»X` provided by `X` and the operand types of `«op»Y` have an identity conversion to the corresponding operand types of `«op»X` then only `«op»X` occurs in the set. - If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. Otherwise, the predefined binary `operator «op»` implementations, including their lifted forms, become the set of candidate operators for the operation. The predefined implementations of a given operator are specified in the description of the operator. For predefined enum and delegate operators, the only operators considered are those provided by an enum or delegate type that is the binding-time type of one of the operands. - The overload resolution rules of [§12.6.4](expressions.md#1264-overload-resolution) are applied to the set of candidate operators to select the best operator with respect to the argument list `(x, y)`, and this operator becomes the result of the overload resolution process. If overload resolution fails to select a single best operator, a binding-time error occurs. @@ -4406,7 +4406,7 @@ The operation is evaluated as follows: 1. Otherwise, `D` is `R`. 1. The result depends on `D` and `T` as follows: 1. If `T` is a reference type, the result is `true` if: - - `D` and `T` are the same type, + - an identity conversion exists between `D` and `T`, - `D` is a reference type and an implicit reference conversion from `D` to `T` exists, or - Either: `D` is a value type and a boxing conversion from `D` to `T` exists. Or: `D` is a value type and `T` is an interface type implemented by `D`. diff --git a/standard/types.md b/standard/types.md index 917bab6fa..8b8a0147f 100644 --- a/standard/types.md +++ b/standard/types.md @@ -396,7 +396,7 @@ There does not need to exist a `System.ValueTuple<...>` declaration that directl Element names within a tuple type shall be distinct. A tuple element name of the form `ItemX`, where `X` is any sequence of non-`0`-initiated decimal digits that could represent the position of a tuple element, is only permitted at the position denoted by `X`. -The optional element names are not represented in the `ValueTuple<...>` types, and are not stored in the runtime representation of a tuple value. There is an identity conversion between all tuple types with the same arity and identity-convertible sequences of element types, as well as to and from the corresponding constructed `ValueTuple<...>` type. +The optional element names are not represented in the `ValueTuple<...>` types, and are not stored in the runtime representation of a tuple value. Identity conversions (§10.2.2) exist between tuples with identity-convertible sequences of element types. The `new` operator [§12.8.16.2](expressions.md#128162-object-creation-expressions) cannot be applied with the tuple type syntax `new (T1, ..., Tn)`. Tuple values can be created from tuple expressions ([§12.8.6](expressions.md#1286-tuple-expressions)), or by applying the `new` operator directly to a type constructed from `ValueTuple<...>`. @@ -546,7 +546,7 @@ A closed type is a type that is not an open type. At run-time, all of the code within a generic type declaration is executed in the context of a closed constructed type that was created by applying type arguments to the generic declaration. Each type parameter within the generic type is bound to a particular run-time type. The run-time processing of all statements and expressions always occurs with closed types, and open types occur only during compile-time processing. -Each closed constructed type has its own set of static variables, which are not shared with any other closed constructed types. Since an open type does not exist at run-time, there are no static variables associated with an open type. Two closed constructed types are the same type if they are constructed from the same unbound generic type, and their corresponding type arguments are the same type. +Two closed constructed types are identity convertible (§10.2.2) if they are constructed from the same unbound generic type, and an identity conversion exists between each of their corresponding type arguments. The corresponding type arguments may themselves be closed constructed types or tuples that are identity convertible. Closed constructed types that are identity convertible share a single set of static variables. Otherwise, each closed constructed type has its own set of static variables. Since an open type does not exist at run-time, there are no static variables associated with an open type. ### 8.4.4 Bound and unbound types @@ -666,12 +666,13 @@ The API surface provided by `Expression` is implementation-specific b The type `dynamic` uses dynamic binding, as described in detail in [§12.3.2](expressions.md#1232-binding-time), as opposed to static binding which is used by all other types. -`dynamic` is considered identical to `object` except in the following respects: +The type `dynamic` is considered identical to `object` except in the following respects: - Operations on expressions of type `dynamic` can be dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). - Type inference ([§12.6.3](expressions.md#1263-type-inference)) will prefer `dynamic` over `object` if both are candidates. - `dynamic` cannot be used as - the type in an *object_creation_expression* ([§12.8.16.2](expressions.md#128162-object-creation-expressions)) + - a *class_base* (§15.2.4) - a *predefined_type* in a *member_access* ([§12.8.7.1](expressions.md#12871-general)) - the operand of the `typeof` operator - an attribute argument @@ -681,10 +682,13 @@ The type `dynamic` uses dynamic binding, as described in detail in [§12.3.2](ex Because of this equivalence, the following holds: -- There is an implicit identity conversion between `object` and `dynamic`, and between constructed types that are the same when replacing `dynamic` with `object`. +- There is an implicit identity conversion + - between `object` and `dynamic` + - between constructed types that are the same when replacing `dynamic` with `object` + - between tuple types that are the same when replacing `dynamic` with `object` - Implicit and explicit conversions to and from `object` also apply to and from `dynamic`. - Signatures that are the same when replacing `dynamic` with `object` are considered the same signature. -- The type `dynamic` is indistinguishable from `object` at run-time. +- The type `dynamic` is indistinguishable from the type `object` at run-time. - An expression of the type `dynamic` is referred to as a ***dynamic expression***. ## 8.8 Unmanaged types