]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types, types2: implement field access for struct structural constraints
authorRobert Griesemer <gri@golang.org>
Wed, 5 Jan 2022 23:46:31 +0000 (15:46 -0800)
committerRobert Griesemer <gri@golang.org>
Thu, 6 Jan 2022 21:38:59 +0000 (21:38 +0000)
This change implements field the access p.f where the type of p
is a type parameter with a structural constraint that is a struct
with a field f. This is only the fix for the type checker. The
compiler will need a separate CL.

This makes the behavior consistent with the fact that we can
write struct composite literals for type parameters with a
struct structural type.

For #50417.
For #50233.

Change-Id: I87d07e016f97cbf19c45cde19165eae3ec0bad2b
Reviewed-on: https://go-review.googlesource.com/c/go/+/375795
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/types2/lookup.go
src/cmd/compile/internal/types2/testdata/fixedbugs/issue50417.go2 [new file with mode: 0644]
src/go/types/lookup.go
src/go/types/testdata/fixedbugs/issue50417.go2 [new file with mode: 0644]
test/typeparam/issue50417.go [new file with mode: 0644]

index 7bdf13b4b7a9b9f700cd2f6fb3f801706680fe3f..77a70a051081dc712bc4cb0730cb0351c6dadc99 100644 (file)
@@ -43,7 +43,7 @@ import (
 //     the method's formal receiver base type, nor was the receiver addressable.
 //
 func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
-       // Methods cannot be associated to a named pointer type
+       // Methods cannot be associated to a named pointer type.
        // (spec: "The type denoted by T is called the receiver base type;
        // it must not be a pointer or interface type and it must be declared
        // in the same package as the method.").
@@ -60,7 +60,21 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
                }
        }
 
-       return lookupFieldOrMethod(T, addressable, false, pkg, name)
+       obj, index, indirect = lookupFieldOrMethod(T, addressable, false, pkg, name)
+
+       // If we didn't find anything and if we have a type parameter with a structural constraint,
+       // see if there is a matching field (but not a method, those need to be declared explicitly
+       // in the constraint). If the structural constraint is a named pointer type (see above), we
+       // are ok here because only fields are accepted as results.
+       if obj == nil && isTypeParam(T) {
+               if t := structuralType(T); t != nil {
+                       obj, index, indirect = lookupFieldOrMethod(t, addressable, false, pkg, name)
+                       if _, ok := obj.(*Var); !ok {
+                               obj, index, indirect = nil, nil, false // accept fields (variables) only
+                       }
+               }
+       }
+       return
 }
 
 // TODO(gri) The named type consolidation and seen maps below must be
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50417.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50417.go2
new file mode 100644 (file)
index 0000000..c05987e
--- /dev/null
@@ -0,0 +1,64 @@
+// Copyright 2022 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 p
+
+type Sf struct {
+        f int
+}
+
+func f0[P Sf](p P) {
+        _ = p.f
+        p.f = 0
+}
+
+func f0t[P ~struct{f int}](p P) {
+        _ = p.f
+        p.f = 0
+}
+
+var _ = f0[Sf]
+var _ = f0t[Sf]
+
+var _ = f0[Sm /* ERROR does not implement */ ]
+var _ = f0t[Sm /* ERROR does not implement */ ]
+
+func f1[P interface{ Sf; m() }](p P) {
+        _ = p.f
+        p.f = 0
+        p.m()
+}
+
+var _ = f1[Sf /* ERROR missing method m */ ]
+var _ = f1[Sm /* ERROR does not implement */ ]
+
+type Sm struct {}
+
+func (Sm) m() {}
+
+type Sfm struct {
+        f int
+}
+
+func (Sfm) m() {}
+
+func f2[P interface{ Sfm; m() }](p P) {
+        _ = p.f
+        p.f = 0
+        p.m()
+}
+
+var _ = f2[Sfm]
+
+// special case: structural type is a named pointer type
+
+type PSfm *Sfm
+
+func f3[P interface{ PSfm }](p P) {
+        _ = p.f
+        p.f = 0
+        p.m /* ERROR type bound for P has no method m */ ()
+}
+
+var _ = f3[PSfm]
index 7f3fbd69290198503df7a06f0c9ad8e010a1f8f9..e59335180460eed27d16129f61b9adb803e32c45 100644 (file)
@@ -43,7 +43,7 @@ import (
 //     the method's formal receiver base type, nor was the receiver addressable.
 //
 func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
-       // Methods cannot be associated to a named pointer type
+       // Methods cannot be associated to a named pointer type.
        // (spec: "The type denoted by T is called the receiver base type;
        // it must not be a pointer or interface type and it must be declared
        // in the same package as the method.").
@@ -60,7 +60,21 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
                }
        }
 
-       return lookupFieldOrMethod(T, addressable, pkg, name)
+       obj, index, indirect = lookupFieldOrMethod(T, addressable, pkg, name)
+
+       // If we didn't find anything and if we have a type parameter with a structural constraint,
+       // see if there is a matching field (but not a method, those need to be declared explicitly
+       // in the constraint). If the structural constraint is a named pointer type (see above), we
+       // are ok here because only fields are accepted as results.
+       if obj == nil && isTypeParam(T) {
+               if t := structuralType(T); t != nil {
+                       obj, index, indirect = lookupFieldOrMethod(t, addressable, pkg, name)
+                       if _, ok := obj.(*Var); !ok {
+                               obj, index, indirect = nil, nil, false // accept fields (variables) only
+                       }
+               }
+       }
+       return
 }
 
 // TODO(gri) The named type consolidation and seen maps below must be
diff --git a/src/go/types/testdata/fixedbugs/issue50417.go2 b/src/go/types/testdata/fixedbugs/issue50417.go2
new file mode 100644 (file)
index 0000000..c05987e
--- /dev/null
@@ -0,0 +1,64 @@
+// Copyright 2022 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 p
+
+type Sf struct {
+        f int
+}
+
+func f0[P Sf](p P) {
+        _ = p.f
+        p.f = 0
+}
+
+func f0t[P ~struct{f int}](p P) {
+        _ = p.f
+        p.f = 0
+}
+
+var _ = f0[Sf]
+var _ = f0t[Sf]
+
+var _ = f0[Sm /* ERROR does not implement */ ]
+var _ = f0t[Sm /* ERROR does not implement */ ]
+
+func f1[P interface{ Sf; m() }](p P) {
+        _ = p.f
+        p.f = 0
+        p.m()
+}
+
+var _ = f1[Sf /* ERROR missing method m */ ]
+var _ = f1[Sm /* ERROR does not implement */ ]
+
+type Sm struct {}
+
+func (Sm) m() {}
+
+type Sfm struct {
+        f int
+}
+
+func (Sfm) m() {}
+
+func f2[P interface{ Sfm; m() }](p P) {
+        _ = p.f
+        p.f = 0
+        p.m()
+}
+
+var _ = f2[Sfm]
+
+// special case: structural type is a named pointer type
+
+type PSfm *Sfm
+
+func f3[P interface{ PSfm }](p P) {
+        _ = p.f
+        p.f = 0
+        p.m /* ERROR type bound for P has no method m */ ()
+}
+
+var _ = f3[PSfm]
diff --git a/test/typeparam/issue50417.go b/test/typeparam/issue50417.go
new file mode 100644 (file)
index 0000000..bf6ac42
--- /dev/null
@@ -0,0 +1,70 @@
+// run
+
+// Copyright 2022 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
+
+func main() {}
+
+type Sf struct {
+       f int
+}
+
+func f0[P Sf](p P) {
+       _ = p.f
+       p.f = 0
+}
+
+func f0t[P ~struct{ f int }](p P) {
+       _ = p.f
+       p.f = 0
+}
+
+// TODO(danscales) enable once the compiler is fixed
+// var _ = f0[Sf]
+// var _ = f0t[Sf]
+
+func f1[P interface {
+       Sf
+       m()
+}](p P) {
+       _ = p.f
+       p.f = 0
+       p.m()
+}
+
+type Sm struct{}
+
+func (Sm) m() {}
+
+type Sfm struct {
+       f int
+}
+
+func (Sfm) m() {}
+
+func f2[P interface {
+       Sfm
+       m()
+}](p P) {
+       _ = p.f
+       p.f = 0
+       p.m()
+}
+
+// TODO(danscales) enable once the compiler is fixed
+// var _ = f2[Sfm]
+
+// special case: structural type is a named pointer type
+
+type PSfm *Sfm
+
+func f3[P interface{ PSfm }](p P) {
+       _ = p.f
+       p.f = 0
+}
+
+// TODO(danscales) enable once the compiler is fixed
+// var _ = f3[PSfm]