go/types, types2: delay receiver type validation

Delay validation of receiver type as it may cause premature expansion
of types the receiver type is dependent on. This was actually a TODO.

While the diff looks large-ish, the actual change is small: all the
receiver validation code has been moved inside the delayed function
body, and a couple of comments have been adjusted.

Fixes #51232.
Fixes #51233.

Change-Id: I44edf0ba615996266791724b832d81b9ccb8b435
Reviewed-on: https://go-review.googlesource.com/c/go/+/387918
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>
This commit is contained in:
Robert Griesemer 2022-02-24 13:35:16 -08:00
parent 55e5b03cb3
commit 7c694fbad1
8 changed files with 274 additions and 99 deletions

View file

@ -194,66 +194,69 @@ func (check *Checker) funcType(sig *Signature, recvPar *syntax.Field, tparams []
case 1:
recv = recvList[0]
}
sig.recv = recv
// TODO(gri) We should delay rtyp expansion to when we actually need the
// receiver; thus all checks here should be delayed to later.
rtyp, _ := deref(recv.typ)
// Delay validation of receiver type as it may cause premature expansion
// of types the receiver type is dependent on (see issues #51232, #51233).
check.later(func() {
rtyp, _ := deref(recv.typ)
// spec: "The receiver type must be of the form T or *T where T is a type name."
// (ignore invalid types - error was reported before)
if rtyp != Typ[Invalid] {
var err string
switch T := rtyp.(type) {
case *Named:
T.resolve(check.bestContext(nil))
// The receiver type may be an instantiated type referred to
// by an alias (which cannot have receiver parameters for now).
if T.TypeArgs() != nil && sig.RecvTypeParams() == nil {
check.errorf(recv.pos, "cannot define methods on instantiated type %s", recv.typ)
break
}
// 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."
if T.obj.pkg != check.pkg {
err = "type not defined in this package"
// spec: "The receiver type must be of the form T or *T where T is a type name."
// (ignore invalid types - error was reported before)
if rtyp != Typ[Invalid] {
var err string
switch T := rtyp.(type) {
case *Named:
T.resolve(check.bestContext(nil))
// The receiver type may be an instantiated type referred to
// by an alias (which cannot have receiver parameters for now).
if T.TypeArgs() != nil && sig.RecvTypeParams() == nil {
check.errorf(recv.pos, "cannot define methods on instantiated type %s", recv.typ)
break
}
// 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."
if T.obj.pkg != check.pkg {
err = "type not defined in this package"
if check.conf.CompilerErrorMessages {
check.errorf(recv.pos, "cannot define new methods on non-local type %s", recv.typ)
err = ""
}
} else {
// The underlying type of a receiver base type can be a type parameter;
// e.g. for methods with a generic receiver T[P] with type T[P any] P.
// TODO(gri) Such declarations are currently disallowed.
// Revisit the need for underIs.
underIs(T, func(u Type) bool {
switch u := u.(type) {
case *Basic:
// unsafe.Pointer is treated like a regular pointer
if u.kind == UnsafePointer {
err = "unsafe.Pointer"
return false
}
case *Pointer, *Interface:
err = "pointer or interface type"
return false
}
return true
})
}
case *Basic:
err = "basic or unnamed type"
if check.conf.CompilerErrorMessages {
check.errorf(recv.pos, "cannot define new methods on non-local type %s", recv.typ)
err = ""
}
} else {
// The underlying type of a receiver base type can be a type parameter;
// e.g. for methods with a generic receiver T[P] with type T[P any] P.
underIs(T, func(u Type) bool {
switch u := u.(type) {
case *Basic:
// unsafe.Pointer is treated like a regular pointer
if u.kind == UnsafePointer {
err = "unsafe.Pointer"
return false
}
case *Pointer, *Interface:
err = "pointer or interface type"
return false
}
return true
})
default:
check.errorf(recv.pos, "invalid receiver type %s", recv.typ)
}
case *Basic:
err = "basic or unnamed type"
if check.conf.CompilerErrorMessages {
check.errorf(recv.pos, "cannot define new methods on non-local type %s", recv.typ)
err = ""
if err != "" {
check.errorf(recv.pos, "invalid receiver type %s (%s)", recv.typ, err)
}
default:
check.errorf(recv.pos, "invalid receiver type %s", recv.typ)
}
if err != "" {
check.errorf(recv.pos, "invalid receiver type %s (%s)", recv.typ, err)
// ok to continue
}
}
sig.recv = recv
}).describef(recv, "validate receiver %s", recv)
}
sig.params = NewTuple(params...)

View file

@ -0,0 +1,29 @@
// 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 RC[RG any] interface {
~[]RG
}
type Fn[RCT RC[RG], RG any] func(RCT)
type F[RCT RC[RG], RG any] interface {
Fn() Fn[RCT]
}
type concreteF[RCT RC[RG], RG any] struct {
makeFn func() Fn[RCT]
}
func (c *concreteF[RCT, RG]) Fn() Fn[RCT] {
return c.makeFn()
}
func NewConcrete[RCT RC[RG], RG any](Rc RCT) F[RCT] {
return &concreteF[RCT]{
makeFn: nil,
}
}

View file

@ -0,0 +1,25 @@
// 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 RC[RG any] interface {
~[]RG
}
type Fn[RCT RC[RG], RG any] func(RCT)
type FFn[RCT RC[RG], RG any] func() Fn[RCT]
type F[RCT RC[RG], RG any] interface {
Fn() Fn[RCT]
}
type concreteF[RCT RC[RG], RG any] struct {
makeFn FFn[RCT]
}
func (c *concreteF[RCT, RG]) Fn() Fn[RCT] {
return c.makeFn()
}

View file

@ -193,66 +193,77 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
switch len(recvList) {
case 0:
// error reported by resolver
recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below
recv = NewParam(token.NoPos, nil, "", Typ[Invalid]) // ignore recv below
default:
// more than one receiver
check.error(recvList[len(recvList)-1], _BadRecv, "method must have exactly one receiver")
check.error(recvList[len(recvList)-1], _InvalidRecv, "method must have exactly one receiver")
fallthrough // continue with first receiver
case 1:
recv = recvList[0]
}
sig.recv = recv
// TODO(gri) We should delay rtyp expansion to when we actually need the
// receiver; thus all checks here should be delayed to later.
rtyp, _ := deref(recv.typ)
// Delay validation of receiver type as it may cause premature expansion
// of types the receiver type is dependent on (see issues #51232, #51233).
check.later(func() {
rtyp, _ := deref(recv.typ)
// spec: "The receiver type must be of the form T or *T where T is a type name."
// (ignore invalid types - error was reported before)
if rtyp != Typ[Invalid] {
var err string
switch T := rtyp.(type) {
case *Named:
T.resolve(check.bestContext(nil))
// The receiver type may be an instantiated type referred to
// by an alias (which cannot have receiver parameters for now).
if T.TypeArgs() != nil && sig.RecvTypeParams() == nil {
check.errorf(atPos(recv.pos), _InvalidRecv, "cannot define methods on instantiated type %s", recv.typ)
break
}
// 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."
if T.obj.pkg != check.pkg {
err = "type not defined in this package"
} else {
// The underlying type of a receiver base type can be a type parameter;
// e.g. for methods with a generic receiver T[P] with type T[P any] P.
underIs(T, func(u Type) bool {
switch u := u.(type) {
case *Basic:
// unsafe.Pointer is treated like a regular pointer
if u.kind == UnsafePointer {
err = "unsafe.Pointer"
// spec: "The receiver type must be of the form T or *T where T is a type name."
// (ignore invalid types - error was reported before)
if rtyp != Typ[Invalid] {
var err string
switch T := rtyp.(type) {
case *Named:
T.resolve(check.bestContext(nil))
// The receiver type may be an instantiated type referred to
// by an alias (which cannot have receiver parameters for now).
if T.TypeArgs() != nil && sig.RecvTypeParams() == nil {
check.errorf(recv, _InvalidRecv, "cannot define methods on instantiated type %s", recv.typ)
break
}
// 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."
if T.obj.pkg != check.pkg {
err = "type not defined in this package"
if compilerErrorMessages {
check.errorf(recv, _InvalidRecv, "cannot define new methods on non-local type %s", recv.typ)
err = ""
}
} else {
// The underlying type of a receiver base type can be a type parameter;
// e.g. for methods with a generic receiver T[P] with type T[P any] P.
// TODO(gri) Such declarations are currently disallowed.
// Revisit the need for underIs.
underIs(T, func(u Type) bool {
switch u := u.(type) {
case *Basic:
// unsafe.Pointer is treated like a regular pointer
if u.kind == UnsafePointer {
err = "unsafe.Pointer"
return false
}
case *Pointer, *Interface:
err = "pointer or interface type"
return false
}
case *Pointer, *Interface:
err = "pointer or interface type"
return false
}
return true
})
return true
})
}
case *Basic:
err = "basic or unnamed type"
if compilerErrorMessages {
check.errorf(recv, _InvalidRecv, "cannot define new methods on non-local type %s", recv.typ)
err = ""
}
default:
check.errorf(recv, _InvalidRecv, "invalid receiver type %s", recv.typ)
}
if err != "" {
check.errorf(recv, _InvalidRecv, "invalid receiver type %s (%s)", recv.typ, err)
}
case *Basic:
err = "basic or unnamed type"
default:
check.errorf(recv, _InvalidRecv, "invalid receiver type %s", recv.typ)
}
if err != "" {
check.errorf(recv, _InvalidRecv, "invalid receiver type %s (%s)", recv.typ, err)
// ok to continue
}
}
sig.recv = recv
}).describef(recv, "validate receiver %s", recv)
}
sig.params = NewTuple(params...)

View file

@ -0,0 +1,29 @@
// 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 RC[RG any] interface {
~[]RG
}
type Fn[RCT RC[RG], RG any] func(RCT)
type F[RCT RC[RG], RG any] interface {
Fn() Fn[RCT]
}
type concreteF[RCT RC[RG], RG any] struct {
makeFn func() Fn[RCT]
}
func (c *concreteF[RCT, RG]) Fn() Fn[RCT] {
return c.makeFn()
}
func NewConcrete[RCT RC[RG], RG any](Rc RCT) F[RCT] {
return &concreteF[RCT]{
makeFn: nil,
}
}

View file

@ -0,0 +1,25 @@
// 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 RC[RG any] interface {
~[]RG
}
type Fn[RCT RC[RG], RG any] func(RCT)
type FFn[RCT RC[RG], RG any] func() Fn[RCT]
type F[RCT RC[RG], RG any] interface {
Fn() Fn[RCT]
}
type concreteF[RCT RC[RG], RG any] struct {
makeFn FFn[RCT]
}
func (c *concreteF[RCT, RG]) Fn() Fn[RCT] {
return c.makeFn()
}

View file

@ -0,0 +1,31 @@
// compile -G=3
// 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 RC[RG any] interface {
~[]RG
}
type Fn[RCT RC[RG], RG any] func(RCT)
type F[RCT RC[RG], RG any] interface {
Fn() Fn[RCT]
}
type concreteF[RCT RC[RG], RG any] struct {
makeFn func() Fn[RCT]
}
func (c *concreteF[RCT, RG]) Fn() Fn[RCT] {
return c.makeFn()
}
func NewConcrete[RCT RC[RG], RG any](Rc RCT) F[RCT] {
return &concreteF[RCT]{
makeFn: nil,
}
}

View file

@ -0,0 +1,22 @@
// compile -G=3
// 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 RC[RG any] interface {
~[]RG
}
type Fn[RCT RC[RG], RG any] func(RCT)
type FFn[RCT RC[RG], RG any] func() Fn[RCT]
type F[RCT RC[RG], RG any] interface {
Fn() Fn[RCT]
}
type concreteF[RCT RC[RG], RG any] struct {
makeFn FFn[RCT]
}
func (c *concreteF[RCT, RG]) Fn() Fn[RCT] {
return c.makeFn()
}