Skip to content

Latest commit

 

History

History
161 lines (81 loc) · 11.2 KB

i.8.7-assignment-compatibility.md

File metadata and controls

161 lines (81 loc) · 11.2 KB

I.8.7 Assignment compatibility

Assignment compatibility refers to the ability to store a value of type T (statically described by a type signature) into a location of type U (described by a location signature), and is sometimes abbreviated U := T. Because the type signature for T is described statically, the value might not actually be of the type described by the signature, but rather something compatible with that type. No location or value shall have type System.Void.

The formal description of assignment compatibility is provided here, and is extended in Partition III, Verification type compatibility, with the verifier-assignable-to relation.

There are different rules for determining the compatibility of types, depending upon the context in which they are evaluated. The following relations are defined in this section:

  • compatible-with – this is the relation used by castclassIII.4.3) and isinstIII.4.6), and in determining the validity of variant generic arguments. [Note: operations based on this relation do not change the representation of a value. When casting, the source type is the dynamic type of the value. end note]

  • assignable-to – this is the relation used for general assignment; including load and store instructions (§III.3), implicit argument coercion (§III.1.6), and method return (§III.3.57). [Note: operations based on this relation may change the representation of a value. When assigning, the source type is the static type of the value. end note]

  • array-element-compatible-with – this is the auxiliary relation used to determine the validity of assignments to array elements

  • pointer-element-compatible-with – this is the auxiliary relation used to determine the compatibility of managed pointers

Informative text.

These relations are defined in terms of six type subsets:

  • storage types – these are the types that can occur as location (§I.8.6.1.2), local (§I.8.6.1.3) and parameter (§I.8.6.1.4) signatures. [Note: method signatures (§I.8.6.1.5) are not included here as there are no method values which can be assigned, delegate types (§I.8.9.3) are reference types (§I.8.2.1) and may occur in the above signatures. end note]

  • underlying types – in the CTS enumerations are alternate names for existing types (§I.8.5.2), termed their underlying type. Except for signature matching (§I.8.5.2) enumerations are treated as their underlying type. This subset is the set of storage types with the enumerations removed.

  • reduced types – a value of value type S can be stored into, or loaded from, an array of value type T; and an array of value type S can be assigned to an array of value type T; if and only if S and T have the same reduced type. The reduced types are a subset of the underlying types.

  • verification types – the verification algorithm treats certain types as interchangeable, assigning them a common verification type. The verification types are a subset of the reduced types.

  • intermediate types – only a subset of the built-in value types can be represented on the evaluation stack (§I.12.1). Values of other built-in value types are translated to/from their intermediate type when loaded onto/stored from the evaluation stack. The intermediate types are a subset of the verification types plus the floating-point type F (which is not a member of the above four subsets).

  • transient types – these are types which can only occur on the evaluation stack: boxed types, controlled-mutability managed pointer types, and the null type. Assignment compatibility for these types is defined by the verifier-assignable-to relation defined in §III.1.8.1.2.3. The precise definitions of underlying type, reduced type, verification type and intermediate type are given below.

End informative text.

Treatment of floating-point types

Floating-point values have two types; the nominal type, and the representation type. There are three floating-point types: float32, float64 and F. A value of (nominal) type float32 or float64 may be represented by an implementation using a value of type F. See §I.12.1.3 for complete details. Unless explicitly indicated any reference to floating-point types refers to the nominal type, in particular when referring to signatures (§I.8.6.1) and assignment compatibility. Consequently when the assignment compatibility rules indicate that a floating-point representation may change based on the (nominal) types the representation types may already be the same and no change is actually performed.

Notation

In the following definitions and relations:

  • S, T, U, V, W represent arbitrary type expressions;

  • N, M represent declared type names; and

  • X, Y represent declared (formal) type parameters.

The notation:

T is of the form N<{XiTi}>

is defined to mean:

T is a possibly-instantiated object, interface, delegate or value type of the form N<T1,…,Tn>, n ≥ 0 (for n = 0 the empty <> are omitted), and N is declared with generic parameters X1,…,Xn

Definitions

The following definitions are used in defining assignment compatibility.

The underlying type of a type T is the following:

  1. If T is an enumeration type, then its underlying type is the underlying type declared in the enumeration's definition.

  2. Otherwise, the underlying type is itself.

The reduced type of a type T is the following:

  1. If the underlying type of T is:

    1. int8, or unsigned int8, then its reduced type is int8.

    2. int16, or unsigned int16, then its reduced type is int16.

    3. int32, or unsigned int32, then its reduced type is int32.

    4. int64, or unsigned int64, then its reduced type is int64.

    5. native int, or unsigned native int, then its reduced type is native int.

  2. Otherwise, the reduced type is itself.

[Note: in other words the reduced type ignores the semantic differences between enumerations and the signed and unsigned integer types; treating these types the same if they have the same number of bits. end note]

The verification typeIII.1.8.1.2.1) of a type T is the following:

  1. If the reduced type of T is:

    1. int8 or bool, then its verification type is int8.

    2. int16 or char, then its verification type is int16.

    3. int32 then its verification type is int32.

    4. int64 then its verification type is int64.

    5. native int, then its verification type is native int.

  2. If T is a managed pointer type S& and the reduced type of S is:

    1. int8 or bool, then its verification type is int8&.

    2. int16 or char, then its verification type is int16&.

    3. int32, then its verification type is int32&.

    4. int64, then its verification type is int64&.

    5. native int, then its verification type is native int&.

  3. Otherwise, the verification type is itself.

[Note: in other words the verification type ignores the semantic differences between enumerations, characters, booleans, the signed and unsigned integer types, and managed pointers to any of these; treating these types the same if they have the same number of bits or point to types with the same number of bits. end note]

The intermediate type of a type T is the following:

  1. If the verification type of T is int8, int16, or int32, then its intermediate type is int32.

  2. If the verification type of T is a floating-point type then its intermediate type is FIII.1.1.1).

  3. Otherwise, the intermediate type is the verification type of T.

[Note: the intermediate type is similar to the verification type in stack state according to the table in III.1.8.1.2.1, differing only for floating-point types. The intermediate type of a type T may have a different representation and meaning than T. end note]

The direct base class of a type T is the following:

  1. If T is an array type (zero-based single-dimensional, or general) then its direct base class is System.Array.

  2. If T is an interface type, then its direct base class is System.Object.

  3. If T is of the form N<{XiTi}>, and N is declared to extend a type U of the form M<{YjSj}>, then the direct base class of T is U with any occurrence of X1,…,Xn in S1,…,Sm replaced by the corresponding T1,…,Tn.

  4. For any other form of type T, there is no direct base class.

[Note: as a result of this definition, only System.Object itself, the unboxed form of a value type, and generic parameters have no direct base class. end note]

The interfaces directly implemented by a type T are the following:

  1. If T is of the form N<{XiTi}> and is declared to implement (or require implementation of, if N is an interface) interfaces U1,…,Um of the form Mj<{Yj,kSj,k}>, then the interfaces directly implemented by T are U1,…,Um with any occurrence of Xi in Sj,k replaced by the corresponding Ti.

  2. For any other form of type T, there are no directly implemented interfaces.

A type T is a reference type if and only if one of the following holds.

  1. T is a possibly-instantiated object, delegate or interface of the form N<T1,…,Tn> (n ≥ 0)

  2. T is an array type

[Note: generic parameters are not reference types. Therefore, the compatibility rules for reference types do not apply. See the definition of verification compatibility in Partition III for the special case of boxed types. end note]

For the purpose of type compatibility when determining a type from a signature:

  1. Any byref (&) constraint (§I.8.6.1.3) is considered part of the type;

  2. The special signature typed reference (§I.8.6.1.3) is the type typedref;

  3. Any modopt, modreq, or pinned modifiers are ignored; and

  4. Any calling convention is considered part of the type.

[Note: the literal constraint is not considered as fields so marked cannot be referenced from CIL (§I.8.6.1.2). end note]