From: Dan Scales Date: Wed, 10 Mar 2021 02:24:51 +0000 (-0800) Subject: cmd/compile: fix handling of partially inferred type arguments X-Git-Tag: go1.17beta1~1189 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=fdded79e6e3256118af182b42714d4d56f2000b0;p=gostls13.git cmd/compile: fix handling of partially inferred type arguments In the case of partially inferred type arguments, we need to use the IndexExpr as the key in g.info.Inferred[] rather than the CallExpr. Added an extra fromStrings1 call in the settable.go test that tests partially inferred type arguments. This new call uses a new concrete type SettableString as well. I also added another implementation fromStrings3 (derived from a go2go tests) that typechecks but intentionally causes a panic. Change-Id: I74d35c5a741f72f37160a96fbec939451157f392 Reviewed-on: https://go-review.googlesource.com/c/go/+/300309 Run-TryBot: Dan Scales TryBot-Result: Go Bot Trust: Dan Scales Trust: Robert Griesemer Reviewed-by: Robert Griesemer --- diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go index b99f5a4cdd..06aa91199c 100644 --- a/src/cmd/compile/internal/noder/expr.go +++ b/src/cmd/compile/internal/noder/expr.go @@ -96,7 +96,17 @@ func (g *irgen) expr0(typ types2.Type, expr syntax.Expr) ir.Node { case *syntax.CallExpr: fun := g.expr(expr.Fun) - if inferred, ok := g.info.Inferred[expr]; ok && len(inferred.Targs) > 0 { + + // The key for the Inferred map is usually the expr. + key := syntax.Expr(expr) + if _, ok := expr.Fun.(*syntax.IndexExpr); ok { + // If the Fun is an IndexExpr, then this may be a + // partial type inference case. In this case, we look up + // the IndexExpr in the Inferred map. + // TODO(gri): should types2 always record the callExpr as the key? + key = syntax.Expr(expr.Fun) + } + if inferred, ok := g.info.Inferred[key]; ok && len(inferred.Targs) > 0 { targs := make([]ir.Node, len(inferred.Targs)) for i, targ := range inferred.Targs { targs[i] = ir.TypeNode(g.typ(targ)) diff --git a/test/typeparam/settable.go b/test/typeparam/settable.go index f42c6574fe..29874fb189 100644 --- a/test/typeparam/settable.go +++ b/test/typeparam/settable.go @@ -11,11 +11,14 @@ import ( "strconv" ) +// Various implementations of fromStrings(). + type _Setter[B any] interface { Set(string) type *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 { @@ -28,6 +31,7 @@ func fromStrings1[T any, PT _Setter[T]](s []string) []T { 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 { @@ -36,24 +40,65 @@ func fromStrings2[T any](s []string, set func(*T, string)) []T { return results } -type Settable int +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 *Settable) Set(s string) { +func (p *SettableInt) Set(s string) { i, err := strconv.Atoi(s) if err != nil { panic(err) } - *p = Settable(i) + *p = SettableInt(i) +} + +type SettableString struct { + s string +} + +func (x *SettableString) Set(s string) { + x.s = s } func main() { - s := fromStrings1[Settable, *Settable]([]string{"1"}) + s := fromStrings1[SettableInt, *SettableInt]([]string{"1"}) if len(s) != 1 || s[0] != 1 { panic(fmt.Sprintf("got %v, want %v", s, []int{1})) } - s = fromStrings2([]string{"1"}, func(p *Settable, s string) { p.Set(s) }) + // 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"}) }