mirror of
https://github.com/golang/go
synced 2024-09-05 16:04:50 +00:00
![Robert Griesemer](/assets/img/avatar_default.png)
When doing constraint type inference, we must consider whether the constraint's core type is precise (no tilde) or imprecise (tilde, or not a single specific type). In the latter case, we cannot infer an unknown type argument from the (imprecise) core type because there are infinitely many possible types. For instance, given [E ~byte] if we don't know E, we cannot infer that E must be byte (it could be myByte, etc.). On the other hand, if we do know the type argument, say for S in this example: [S ~[]E, E any] we must consider the underlying type of S when matching against ~[]E because we have a tilde. Because constraint type inference may infer type arguments that were not eligible initially (because they were unknown and the core type is imprecise), we must iterate the process until nothing changes any- more. For instance, given [S ~[]E, M ~map[string]S, E any] where we initially only know the type argument for M, we must ignore S (and E) at first. After one iteration of constraint type inference, S is known at which point we can infer E as well. The change is large-ish but the actual functional changes are small: - There's a new method "unknowns" to determine the number of as of yet unknown type arguments. - The adjCoreType function has been adjusted to also return tilde and single-type information. This is now conveniently returned as (*term, bool), and the function has been renamed to coreTerm. - The original constraint type inference loop has been adjusted to consider tilde information. - This adjusted original constraint type inference loop has been nested in another loop for iteration, together with some minimal logic to control termination. The remaining changes are modifications to tests: - There's a substantial new test for this issue. - Several existing test cases were adjusted to accomodate the fact that they inferred incorrect types: tildes have been removed throughout. Most of these tests are for pathological cases. - A couple of tests were adjusted where there was a difference between the go/types and types2 version. Fixes #51229. Change-Id: If0bf5fb70ec22913b5a2da89adbf8a27fbc921d9 Reviewed-on: https://go-review.googlesource.com/c/go/+/387977 Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
124 lines
2.7 KiB
Go
124 lines
2.7 KiB
Go
// run
|
|
|
|
// Copyright 2021 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
)
|
|
|
|
// Various implementations of fromStrings().
|
|
|
|
type Setter[B any] interface {
|
|
Set(string)
|
|
*B
|
|
}
|
|
|
|
// Takes two type parameters where PT = *T
|
|
func fromStrings1[T any, PT Setter[T]](s []string) []T {
|
|
result := make([]T, len(s))
|
|
for i, v := range s {
|
|
// The type of &result[i] is *T which is in the type list
|
|
// of Setter, so we can convert it to PT.
|
|
p := PT(&result[i])
|
|
// PT has a Set method.
|
|
p.Set(v)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func fromStrings1a[T any, PT Setter[T]](s []string) []PT {
|
|
result := make([]PT, len(s))
|
|
for i, v := range s {
|
|
// The type new(T) is *T which is in the type list
|
|
// of Setter, so we can convert it to PT.
|
|
result[i] = PT(new(T))
|
|
p := result[i]
|
|
// PT has a Set method.
|
|
p.Set(v)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Takes one type parameter and a set function
|
|
func fromStrings2[T any](s []string, set func(*T, string)) []T {
|
|
results := make([]T, len(s))
|
|
for i, v := range s {
|
|
set(&results[i], v)
|
|
}
|
|
return results
|
|
}
|
|
|
|
type Setter2 interface {
|
|
Set(string)
|
|
}
|
|
|
|
// Takes only one type parameter, but causes a panic (see below)
|
|
func fromStrings3[T Setter2](s []string) []T {
|
|
results := make([]T, len(s))
|
|
for i, v := range s {
|
|
// Panics if T is a pointer type because receiver is T(nil).
|
|
results[i].Set(v)
|
|
}
|
|
return results
|
|
}
|
|
|
|
// Two concrete types with the appropriate Set method.
|
|
|
|
type SettableInt int
|
|
|
|
func (p *SettableInt) Set(s string) {
|
|
i, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
*p = SettableInt(i)
|
|
}
|
|
|
|
type SettableString struct {
|
|
s string
|
|
}
|
|
|
|
func (x *SettableString) Set(s string) {
|
|
x.s = s
|
|
}
|
|
|
|
func main() {
|
|
s := fromStrings1[SettableInt, *SettableInt]([]string{"1"})
|
|
if len(s) != 1 || s[0] != 1 {
|
|
panic(fmt.Sprintf("got %v, want %v", s, []int{1}))
|
|
}
|
|
|
|
s2 := fromStrings1a[SettableInt, *SettableInt]([]string{"1"})
|
|
if len(s2) != 1 || *s2[0] != 1 {
|
|
x := 1
|
|
panic(fmt.Sprintf("got %v, want %v", s2, []*int{&x}))
|
|
}
|
|
|
|
// Test out constraint type inference, which should determine that the second
|
|
// type param is *SettableString.
|
|
ps := fromStrings1[SettableString]([]string{"x", "y"})
|
|
if len(ps) != 2 || ps[0] != (SettableString{"x"}) || ps[1] != (SettableString{"y"}) {
|
|
panic(s)
|
|
}
|
|
|
|
s = fromStrings2([]string{"1"}, func(p *SettableInt, s string) { p.Set(s) })
|
|
if len(s) != 1 || s[0] != 1 {
|
|
panic(fmt.Sprintf("got %v, want %v", s, []int{1}))
|
|
}
|
|
|
|
defer func() {
|
|
if recover() == nil {
|
|
panic("did not panic as expected")
|
|
}
|
|
}()
|
|
// This should type check but should panic at run time,
|
|
// because it will make a slice of *SettableInt and then call
|
|
// Set on a nil value.
|
|
fromStrings3[*SettableInt]([]string{"1"})
|
|
}
|