]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: fix handling of partially inferred type arguments
authorDan Scales <danscales@google.com>
Wed, 10 Mar 2021 02:24:51 +0000 (18:24 -0800)
committerDan Scales <danscales@google.com>
Thu, 11 Mar 2021 00:31:14 +0000 (00:31 +0000)
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 <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/cmd/compile/internal/noder/expr.go
test/typeparam/settable.go

index b99f5a4cdd92edafa3777bb4ce2783479dd410a0..06aa91199c6bcd0d1f5d2f4b54a7aa6b075ba49b 100644 (file)
@@ -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))
index f42c6574fea0843008fb1005ab86e7a9de31090b..29874fb189b313042c8ff1992b5845fbab59e65e 100644 (file)
@@ -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"})
 }