]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: add Func.Signature method
authorAlan Donovan <adonovan@google.com>
Tue, 20 Feb 2024 15:24:21 +0000 (10:24 -0500)
committerAlan Donovan <adonovan@google.com>
Thu, 18 Apr 2024 22:17:27 +0000 (22:17 +0000)
Unfortunately we can't enforce the repr invariant
that Func.typ != nil without thinking about the
object color invariants. For now, return a trivial
Signature if typ == nil, which should never happen
in bug-free client code.

Fixes golang/go#65772

Change-Id: I7f89c6d8fdc8dcd4b8880572e54bb0ed9b6136eb
Reviewed-on: https://go-review.googlesource.com/c/go/+/565375
Commit-Queue: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

api/next/65772.txt [new file with mode: 0644]
doc/next/6-stdlib/99-minor/go/types/65772.md [new file with mode: 0644]
src/cmd/compile/internal/types2/object.go
src/cmd/compile/internal/types2/subst.go
src/go/types/api_test.go
src/go/types/issues_test.go
src/go/types/object.go
src/go/types/resolver.go
src/go/types/subst.go

diff --git a/api/next/65772.txt b/api/next/65772.txt
new file mode 100644 (file)
index 0000000..1244df8
--- /dev/null
@@ -0,0 +1 @@
+pkg go/types, method (*Func) Signature() *Signature #65772
diff --git a/doc/next/6-stdlib/99-minor/go/types/65772.md b/doc/next/6-stdlib/99-minor/go/types/65772.md
new file mode 100644 (file)
index 0000000..33e949d
--- /dev/null
@@ -0,0 +1,4 @@
+The [`Func`](/go/types#Func) type, which represents a function or
+method symbol, now has a [`Signature`](/go/types#Func.Signature)
+method that returns the function's type, which is always a
+`Signature`.
\ No newline at end of file
index 776986f1112ea208b74f7d1c57aa4662dabd27ff..3026777cada84c357f0110df6cf70bf9497703b2 100644 (file)
@@ -374,14 +374,34 @@ type Func struct {
 // NewFunc returns a new function with the given signature, representing
 // the function's type.
 func NewFunc(pos syntax.Pos, pkg *Package, name string, sig *Signature) *Func {
-       // don't store a (typed) nil signature
        var typ Type
        if sig != nil {
                typ = sig
+       } else {
+               // Don't store a (typed) nil *Signature.
+               // We can't simply replace it with new(Signature) either,
+               // as this would violate object.{Type,color} invariants.
+               // TODO(adonovan): propose to disallow NewFunc with nil *Signature.
        }
        return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, false, nil}
 }
 
+// Signature returns the signature (type) of the function or method.
+func (obj *Func) Signature() *Signature {
+       if obj.typ != nil {
+               return obj.typ.(*Signature) // normal case
+       }
+       // No signature: Signature was called either:
+       // - within go/types, before a FuncDecl's initially
+       //   nil Func.Type was lazily populated, indicating
+       //   a types bug; or
+       // - by a client after NewFunc(..., nil),
+       //   which is arguably a client bug, but we need a
+       //   proposal to tighten NewFunc's precondition.
+       // For now, return a trivial signature.
+       return new(Signature)
+}
+
 // FullName returns the package- or receiver-type-qualified name of
 // function or method obj.
 func (obj *Func) FullName() string {
index fa636a1e1ef19608e3427f051212204be0d636f3..215d1f2d4f660841ddb5940fed68a6a30dbfafd2 100644 (file)
@@ -421,7 +421,7 @@ func (subst *subster) termlist(in []*Term) (out []*Term, copied bool) {
 func replaceRecvType(in []*Func, old, new Type) (out []*Func, copied bool) {
        out = in
        for i, method := range in {
-               sig := method.Type().(*Signature)
+               sig := method.Signature()
                if sig.recv != nil && sig.recv.Type() == old {
                        if !copied {
                                // Allocate a new methods slice before mutating for the first time.
index 5bc4e8a61f04dba434451d178cca0ddb3a3df4ce..564bbc2423c0135886a4006e4adf58caaea49a61 100644 (file)
@@ -2324,7 +2324,7 @@ func f(x int) { y := x; print(y) }
                                wantParent = false
                        }
                case *Func:
-                       if obj.Type().(*Signature).Recv() != nil { // method
+                       if obj.Signature().Recv() != nil { // method
                                wantParent = false
                        }
                }
@@ -2615,9 +2615,9 @@ func fn() {
 
                // Methods and method fields
                {"concreteMethod", lookup("t").(*Named).Method(0)},
-               {"recv", lookup("t").(*Named).Method(0).Type().(*Signature).Recv()},
-               {"mParam", lookup("t").(*Named).Method(0).Type().(*Signature).Params().At(0)},
-               {"mResult", lookup("t").(*Named).Method(0).Type().(*Signature).Results().At(0)},
+               {"recv", lookup("t").(*Named).Method(0).Signature().Recv()},
+               {"mParam", lookup("t").(*Named).Method(0).Signature().Params().At(0)},
+               {"mResult", lookup("t").(*Named).Method(0).Signature().Results().At(0)},
 
                // Interface methods
                {"interfaceMethod", lookup("i").Underlying().(*Interface).Method(0)},
index 4f4bf6f07704135fa19f4f6c4ba11911cdd65f0a..379d833bf21f6955b74f8d699c87caf4a7412a1d 100644 (file)
@@ -115,7 +115,7 @@ type T struct{} // receiver type after method declaration
        }
 
        m := f.Decls[0].(*ast.FuncDecl)
-       res1 := defs[m.Name].(*Func).Type().(*Signature).Results().At(0)
+       res1 := defs[m.Name].(*Func).Signature().Results().At(0)
        res2 := defs[m.Type.Results.List[0].Names[0]].(*Var)
 
        if res1 != res2 {
@@ -369,7 +369,7 @@ func TestIssue28005(t *testing.T) {
                // must match the method's name per the choice in the source file.
                for i := 0; i < iface.NumMethods(); i++ {
                        m := iface.Method(i)
-                       recvName := m.Type().(*Signature).Recv().Type().(*Named).Obj().Name()
+                       recvName := m.Signature().Recv().Type().(*Named).Obj().Name()
                        if recvName != m.Name() {
                                t.Errorf("perm %v: got recv %s; want %s", perm, recvName, m.Name())
                        }
index 400b47ebe10c55440552c2aa3093ca69168334d1..d564d37e7a6f1a54e61f92d5bfea2f0a53c72814 100644 (file)
@@ -377,14 +377,34 @@ type Func struct {
 // NewFunc returns a new function with the given signature, representing
 // the function's type.
 func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func {
-       // don't store a (typed) nil signature
        var typ Type
        if sig != nil {
                typ = sig
+       } else {
+               // Don't store a (typed) nil *Signature.
+               // We can't simply replace it with new(Signature) either,
+               // as this would violate object.{Type,color} invariants.
+               // TODO(adonovan): propose to disallow NewFunc with nil *Signature.
        }
        return &Func{object{nil, pos, pkg, name, typ, 0, colorFor(typ), nopos}, false, nil}
 }
 
+// Signature returns the signature (type) of the function or method.
+func (obj *Func) Signature() *Signature {
+       if obj.typ != nil {
+               return obj.typ.(*Signature) // normal case
+       }
+       // No signature: Signature was called either:
+       // - within go/types, before a FuncDecl's initially
+       //   nil Func.Type was lazily populated, indicating
+       //   a types bug; or
+       // - by a client after NewFunc(..., nil),
+       //   which is arguably a client bug, but we need a
+       //   proposal to tighten NewFunc's precondition.
+       // For now, return a trivial signature.
+       return new(Signature)
+}
+
 // FullName returns the package- or receiver-type-qualified name of
 // function or method obj.
 func (obj *Func) FullName() string {
index f336057c531dee4a33ef877fde4c5d67fdb47975..918e18de3e6446726988085b8fadc421f27bc061 100644 (file)
@@ -395,8 +395,8 @@ func (check *Checker) collectObjects() {
                                check.declarePkgObj(d.spec.Name, obj, &declInfo{file: fileScope, tdecl: d.spec})
                        case funcDecl:
                                name := d.decl.Name.Name
-                               obj := NewFunc(d.decl.Name.Pos(), pkg, name, nil)
-                               hasTParamError := false // avoid duplicate type parameter errors
+                               obj := NewFunc(d.decl.Name.Pos(), pkg, name, nil) // signature set later
+                               hasTParamError := false                           // avoid duplicate type parameter errors
                                if d.decl.Recv.NumFields() == 0 {
                                        // regular function
                                        if d.decl.Recv != nil {
index 1d180abb65e3d190fbfb70ad747edcff8ecb54c2..a3ea16d9b99999aeab973dd37b20ee9ab2c56e78 100644 (file)
@@ -424,7 +424,7 @@ func (subst *subster) termlist(in []*Term) (out []*Term, copied bool) {
 func replaceRecvType(in []*Func, old, new Type) (out []*Func, copied bool) {
        out = in
        for i, method := range in {
-               sig := method.Type().(*Signature)
+               sig := method.Signature()
                if sig.recv != nil && sig.recv.Type() == old {
                        if !copied {
                                // Allocate a new methods slice before mutating for the first time.