diff --git a/doc/go_spec.html b/doc/go_spec.html index b25cf5fa6e..c653cbffc0 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ @@ -4128,8 +4128,8 @@ with the same underlying array.
-A parameterized function or type is instantiated by substituting -type arguments for the type parameters. +A parameterized function or type is instantiated by substituting type arguments +for the type parameters. Instantiation proceeds in two phases:
@@ -4201,6 +4201,385 @@ b := min[float64](2.0, 3) // b has value 2.0 of type float64 c := min(b, -1) // c has value -1.0 of type float64 ++Missing type arguments may be inferred by a series of steps, described below. +Each step attempts to use known information to infer additional type arguments. +Type inference stops as soon as all type arguments are known. +After type inference is complete, it is still necessary to substitute all type arguments +for type parameters and verify that each type argument implements the relevant constraint; +it is possible for an inferred type argument to fail to implement a constraint, in which +case instantiation fails. +
+ ++Type inference is based on +
+ ++and then proceeds with the following steps: +
+ ++If there are no ordinary or untyped function arguments, the respective steps are skipped. +Constraint type inference is skipped if the previous step didn't infer any new type arguments, +but it is run at least once if there are missing type arguments. +
+ ++The substitution map M is carried through all steps, and each step may add entries to M. +The process stops as soon as M has a type argument for each type parameter or if an inference step fails. +If an inference step fails, or if M is still missing type arguments after the last step, type inference fails. +
+ +
+Type inference is based on type unification. A single unification step
+applies to a substitution map and two types, either
+or both of which may be or contain type parameters. The substitution map tracks
+the known (explicitly provided or already inferred) type arguments: the map
+contains an entry P → A for each type
+parameter P and corresponding known type argument A.
+During unification, known type arguments take the place of their corresponding type
+parameters when comparing types. Unification is the process of finding substitution
+map entries that make the two types equivalent.
+
+For unification, two types that don't contain any type parameters from the current type +parameter list are equivalent +if they are identical, or if they are channel types that are identical ignoring channel +direction, or if their underlying types are equivalent. +
+ ++Unification works by comparing the structure of pairs of types: their structure +disregarding type parameters must be identical, and types other than type parameters +must be equivalent. +A type parameter in one type may match any complete subtype in the other type; +each successful match causes an entry to be added to the substitution map. +If the structure differs, or types other than type parameters are not equivalent, +unification fails. +
+ + + +
+For example, if T1 and T2 are type parameters,
+[]map[int]bool can be unified with any of the following:
+
+[]map[int]bool // types are identical +T1 // adds T1 → []map[int]bool to substitution map +[]T1 // adds T1 → map[int]bool to substitution map +[]map[T1]T2 // adds T1 → int and T2 → bool to substitution map ++ +
+On the other hand, []map[int]bool cannot be unified with any of
+
+int // int is not a slice
+struct{} // a struct is not a slice
+[]struct{} // a struct is not a map
+[]map[T1]string // map element types don't match
+
+
+
+As an exception to this general rule, because a defined type
+D and a type literal L are never equivalent,
+unification compares the underlying type of D with L instead.
+For example, given the defined type
+
+type Vector []float64 ++ +
+and the type literal []E, unification compares []float64 with
+[]E and adds an entry E → float64 to
+the substitution map.
+
+Function argument type inference infers type arguments from function arguments:
+if a function parameter is declared with a type T that uses
+type parameters,
+unifying the type of the corresponding
+function argument with T may infer type arguments for the type
+parameters used by T.
+
+For instance, given the type-parameterized function +
+ ++func scale[Number ~int64|~float64|~complex128](v []Number, s Number) []Number ++ +
+and the call +
+ ++var vector []float64 +scaledVector := scale(vector, 42) ++ +
+the type argument for Number can be inferred from the function argument
+vector by unifying the type of vector with the corresponding
+parameter type: []float64 and []Number
+match in structure and float64 matches with Number.
+This adds the entry Number → float64 to the
+substitution map.
+Untyped arguments, such as the second function argument 42 here, are ignored
+in the first round of function argument type inference and only considered if there are
+unresolved type parameters left.
+
+Function argument type inference can be used when the function has ordinary parameters +whose types are defined using the function's type parameters. Inference happens in two +separate phases; each phase operates on a specific list of (parameter, argument) pairs: +
+ ++Any other (parameter, argument) pair is ignored. +
+ ++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: +
+ ++Example: +
+ ++func min[T constraints.Ordered](x, y T) T + +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) ++ +
+Constraint type inference infers type arguments from already known
+type arguments by considering structural type constraints:
+if the structural type T of a structural constraint is parameterized,
+unifying a known type argument with T may
+infer type arguments for other type parameters used by the structural type.
+
+For instance, consider the type parameter list with type parameters List and
+Elem:
+
+[List ~[]Elem, Elem any] ++ +
+Constraint type inference can deduce the type of Elem from the type argument
+for List because Elem is a type parameter in the structural constraint
+~[]Elem for List.
+If the type argument is Bytes:
+
+type Bytes []byte ++ +
+unifying the underlying type of Bytes with the structural constraint means
+unifying []byte with []Elem. That unification succeeds and yields
+the substitution map entry
+Elem → byte.
+Thus, in this example, constraint type inference can infer the second type argument from the
+first one.
+
+Generally, constraint type inference proceeds in two phases: Starting with a given +substitution map M +
+ +P → A in M where A is or
+contains type parameters Q for which there exist entries
+Q → B in M, substitute those
+Q with the respective B in A.
+Stop when no further substitution is possible.
+
+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 A → int.
+
+In the first phase, the type parameters B and C are unified
+with the structural 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:
+
+A → int,
+B → []C,
+C → *A
+
+Replace A on the right-hand side of → with int:
+
+A → int,
+B → []C,
+C → *int
+
+Replace C on the right-hand side of → with *int:
+
+A → int,
+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.
+