doc/go_spec.html | 695 +++++++++++++++++++++++++++++------------------------ diff --git a/doc/go_spec.html b/doc/go_spec.html index c2fa871eaad8e5ea26aa0bceeb7475e6a8afe949..d1b8bf2a9171bb26c2b60c682f9124f44f52089f 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ @@ -2511,7 +2511,7 @@

Type definitions

A type definition creates a new, distinct type with the same -underlying type and operations as the given type +underlying type and operations as the given type and binds an identifier, the type name, to it.

@@ -4343,7 +4343,7 @@

When using a generic function, type arguments may be provided explicitly, or they may be partially or completely inferred from the context in which the function is used. -Provided that they can be inferred, type arguments may be omitted entirely if the function is: +Provided that they can be inferred, type argument lists may be omitted entirely if the function is:

-Any other (parameter, argument) pair is ignored. +Additionally, each type parameter Pk and corresponding type constraint +Ck yields the type equation +PkC Ck.

-By construction, the arguments of the pairs in Lu are untyped constants -(or the untyped boolean result of a comparison). And because default types -of untyped values are always predeclared non-composite types, they can never match against -a composite type, so it is sufficient to only consider parameter types that are single type -parameters. -

- -

-Each list is processed in a separate phase: +Type inference gives precedence to type information obtained from typed operands +before considering untyped constants. +Therefore, inference proceeds in two phases:

  1. - In the first phase, the parameter and argument types of each pair in Lt - are unified. If unification succeeds for a pair, it may yield new entries that - are added to the substitution map M. If unification fails, type inference - fails. +

    + The type equations are solved for the bound + type parameters using type unification. + If unification fails, type inference fails. +

  2. - The second phase considers the entries of list Lu. Type parameters for - which the type argument has already been determined are ignored in this phase. - For each remaining pair, the parameter type (which is a single type parameter) and - the default type of the corresponding untyped argument is - unified. If unification fails, type inference fails. +

    + For each bound type parameter Pk for which no type argument + has been inferred yet and for which one or more pairs + (cj, Pk) with that same type parameter + were collected, determine the constant kind + of the constants cj in all those pairs the same way as for + constant expressions. + The type argument for Pk is the + default type for the determined constant kind. + If a constant kind cannot be determined due to conflicting constant kinds, + type inference fails. +

-While unification is successful, processing of each list continues until all list elements -are considered, even if all type arguments are inferred before the last list element has -been processed. +If not all type arguments have been found after these two phases, type inference fails.

-Example: +If the two phases are successful, type inference determined a type argument for each +bound type parameter:

-func min[T ~int|~float64](x, y T) T
+	Pk ➞ Ak
+
-var x int -min(x, 2.0) // T is int, inferred from typed argument x; 2.0 is assignable to int -min(1.0, 2.0) // T is float64, inferred from default type for 1.0 and matches default type for 2.0 -min(1.0, 2) // illegal: default type float64 (for 1.0) doesn't match default type int (for 2) - +

+A type argument Ak may be a composite type, +containing other bound type parameters Pk as element types +(or even be just another bound type parameter). +In a process of repeated simplification, the bound type parameters in each type +argument are substituted with the respective type arguments for those type +parameters until each type argument is free of bound type parameters. +

-In the example min(1.0, 2), processing the function argument 1.0 -yields the substitution map entry Tfloat64. Because -processing continues until all untyped arguments are considered, an error is reported. This -ensures that type inference does not depend on the order of the untyped arguments. +If type arguments contain cyclic references to themselves +through bound type parameters, simplification and thus type +inference fails. +Otherwise, type inference succeeds.

-

Constraint type inference

+

Type unification

-Constraint type inference infers type arguments by considering type constraints. -If a type parameter P has a constraint with a -core type C, -unifying P with C -may infer additional type arguments, either the type argument for P, -or if that is already known, possibly the type arguments for type parameters -used in C. +Type inference solves type equations through type unification. +Type unification recursively compares the LHS and RHS types of an +equation, where either or both types may be or contain bound type parameters, +and looks for type arguments for those type parameters such that the LHS +and RHS match (become identical or assignment-compatible, depending on +context). +To that effect, type inference maintains a map of bound type parameters +to inferred type arguments; this map is consulted and updated during type unification. +Initially, the bound type parameters are known but the map is empty. +During type unification, if a new type argument A is inferred, +the respective mapping P ➞ A from type parameter to argument +is added to the map. +Conversely, when comparing types, a known type argument +(a type argument for which a map entry already exists) +takes the place of its corresponding type parameter. +As type inference progresses, the map is populated more and more +until all equations have been considered, or until unification fails. +Type inference succeeds if no unification step fails and the map has +an entry for each type parameter.

-

-For instance, consider the type parameter list with type parameters List and -Elem: + +For example, given the type equation with the bound type parameter +P

-[List ~[]Elem, Elem any]
+	[10]struct{ elem P, list []P } ≡A [10]struct{ elem string; list []string }
 

-Constraint type inference can deduce the type of Elem from the type argument -for List because Elem is a type parameter in the core type -[]Elem of List. -If the type argument is Bytes: +type inference starts with an empty map. +Unification first compares the top-level structure of the LHS and RHS +types. +Both are arrays of the same length; they unify if the element types unify. +Both element types are structs; they unify if they have +the same number of fields with the same names and if the +field types unify. +The type argument for P is not known yet (there is no map entry), +so unifying P with string adds +the mapping P ➞ string to the map. +Unifying the types of the list field requires +unifying []P and []string and +thus P and string. +Since the type argument for P is known at this point +(there is a map entry for P), its type argument +string takes the place of P. +And since string is identical to string, +this unification step succeeds as well. +Unification of the LHS and RHS of the equation is now finished. +Type inference succeeds because there is only one type equation, +no unification step failed, and the map is fully populated.

-
-type Bytes []byte
-
-

-unifying the underlying type of Bytes with the core type means -unifying []byte with []Elem. That unification succeeds and yields -the substitution map entry -Elembyte. -Thus, in this example, constraint type inference can infer the second type argument from the -first one. +Unification uses a combination of exact and loose +unification depending on whether two types have to be +identical, +assignment-compatible, or +only structurally equal. +The respective type unification rules +are spelled out in detail in the Appendix.

-Using the core type of a constraint may lose some information: In the (unlikely) case that -the constraint's type set contains a single defined type -N, the corresponding core type is N's underlying type rather than -N itself. In this case, constraint type inference may succeed but instantiation -will fail because the inferred type is not in the type set of the constraint. -Thus, constraint type inference uses the adjusted core type of -a constraint: if the type set contains a single type, use that type; otherwise use the -constraint's core type. +For an equation of the form X ≡A Y, +where X and Y are types involved +in an assignment (including parameter passing and return statements), +the top-level type structures may unify loosely but element types +must unify exactly, matching the rules for assignments.

-Generally, constraint type inference proceeds in two phases: Starting with a given -substitution map M +For an equation of the form P ≡C C, +where P is a type parameter and C +its corresponding constraint, the unification rules are bit +more complicated:

-
    +
+

-The result of constraint type inference is the final substitution map M from type -parameters P to type arguments A where no type parameter P -appears in any of the A. -

- -

-For instance, given the type parameter list -

- -
-[A any, B []C, C *A]
-
- -

-and the single provided type argument int for type parameter A, -the initial substitution map M contains the entry Aint. -

- -

-In the first phase, the type parameters B and C are unified -with the core type of their respective constraints. This adds the entries -B[]C and C*A -to M. - -

-At this point there are two entries in M where the right-hand side -is or contains type parameters for which there exists other entries in M: -[]C and *A. -In the second phase, these type parameters are replaced with their respective -types. It doesn't matter in which order this happens. Starting with the state -of M after the first phase: -

- -

-Aint, -B[]C, -C*A -

- -

-Replace A on the right-hand side of → with int: -

- -

-Aint, -B[]C, -C*int -

- -

-Replace C on the right-hand side of → with *int: -

- -

-Aint, -B[]*int, -C*int -

- -

-At this point no further substitution is possible and the map is full. -Therefore, M represents the final map of type parameters -to type arguments for the given type parameter list. +When solving type equations from type constraints, +solving one equation may infer additional type arguments, +which in turn may enable solving other equations that depend +on those type arguments. +Type inference repeats type unification as long as new type +arguments are inferred.

Operators

@@ -5479,7 +5400,7 @@
  • ignoring struct tags (see below), x's type and T are not type parameters but have - identical underlying types. + identical underlying types.
  • ignoring struct tags (see below), @@ -7324,7 +7245,8 @@ clear(t) type parameter see below

    -If the argument type is a type parameter, +If the type of the argument to clear is a +type parameter, all types in its type set must be maps or slices, and clear performs the operation corresponding to the actual type argument.

    @@ -8290,7 +8212,7 @@

    A Pointer is a pointer type but a Pointer value may not be dereferenced. -Any pointer or value of underlying type uintptr can be +Any pointer or value of underlying type uintptr can be converted to a type of underlying type Pointer and vice versa. The effect of converting between Pointer and uintptr is implementation-defined.

    @@ -8438,3 +8360,148 @@

    A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory.

    + +

    Appendix

    + +

    Type unification rules

    + +

    +The type unification rules describe if and how two types unify. +The precise details are relevant for Go implementations, +affect the specifics of error messages (such as whether +a compiler reports a type inference or other error), +and may explain why type inference fails in unusual code situations. +But by and large these rules can be ignored when writing Go code: +type inference is designed to mostly "work as expected", +and the unification rules are fine-tuned accordingly. +

    + +

    +Type unification is controlled by a matching mode, which may +be exact or loose. +As unification recursively descends a composite type structure, +the matching mode used for elements of the type, the element matching mode, +remains the same as the matching mode except when two types are unified for +assignability (A): +in this case, the matching mode is loose at the top level but +then changes to exact for element types, reflecting the fact +that types don't have to be identical to be assignable. +

    + +

    +Two types that are not bound type parameters unify exactly if any of +following conditions is true: +

    + + + +

    +If both types are bound type parameters, they unify per the given +matching modes if: +

    + + + +

    +A single bound type parameter P and another type T unify +per the given matching modes if: +

    + + + +

    +Finally, two types that are not bound type parameters unify loosely +(and per the element matching mode) if: +

    + +