mirror of
https://github.com/golang/go
synced 2024-11-24 05:20:04 -07:00
cmd/compile/internal/types2: ensure named types are expanded after type-checking
This is a clean port of CL 356490 from go/types to types2. Fixes #48703. Fixes #48974. Change-Id: I08c0db0b92250cbb043325541b21a577726b40ca Reviewed-on: https://go-review.googlesource.com/c/go/+/356515 Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
3a07ab70a2
commit
a73c6cf762
@ -132,6 +132,7 @@ type Checker struct {
|
|||||||
untyped map[syntax.Expr]exprInfo // map of expressions without final type
|
untyped map[syntax.Expr]exprInfo // map of expressions without final type
|
||||||
delayed []action // stack of delayed action segments; segments are processed in FIFO order
|
delayed []action // stack of delayed action segments; segments are processed in FIFO order
|
||||||
objPath []Object // path of object dependencies during type inference (for cycle reporting)
|
objPath []Object // path of object dependencies during type inference (for cycle reporting)
|
||||||
|
defTypes []*Named // defined types created during type checking, for final validation.
|
||||||
|
|
||||||
// context within which the current object is type-checked
|
// context within which the current object is type-checked
|
||||||
// (valid only for the duration of type-checking a specific object)
|
// (valid only for the duration of type-checking a specific object)
|
||||||
@ -302,6 +303,9 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
|
|||||||
print("== processDelayed ==")
|
print("== processDelayed ==")
|
||||||
check.processDelayed(0) // incl. all functions
|
check.processDelayed(0) // incl. all functions
|
||||||
|
|
||||||
|
print("== expandDefTypes ==")
|
||||||
|
check.expandDefTypes()
|
||||||
|
|
||||||
print("== initOrder ==")
|
print("== initOrder ==")
|
||||||
check.initOrder()
|
check.initOrder()
|
||||||
|
|
||||||
@ -321,6 +325,7 @@ func (check *Checker) checkFiles(files []*syntax.File) (err error) {
|
|||||||
check.pkgPathMap = nil
|
check.pkgPathMap = nil
|
||||||
check.seenPkgMap = nil
|
check.seenPkgMap = nil
|
||||||
check.recvTParamMap = nil
|
check.recvTParamMap = nil
|
||||||
|
check.defTypes = nil
|
||||||
|
|
||||||
// TODO(gri) There's more memory we should release at this point.
|
// TODO(gri) There's more memory we should release at this point.
|
||||||
|
|
||||||
@ -347,6 +352,29 @@ func (check *Checker) processDelayed(top int) {
|
|||||||
check.delayed = check.delayed[:top]
|
check.delayed = check.delayed[:top]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (check *Checker) expandDefTypes() {
|
||||||
|
// Ensure that every defined type created in the course of type-checking has
|
||||||
|
// either non-*Named underlying, or is unresolved.
|
||||||
|
//
|
||||||
|
// This guarantees that we don't leak any types whose underlying is *Named,
|
||||||
|
// because any unresolved instances will lazily compute their underlying by
|
||||||
|
// substituting in the underlying of their origin. The origin must have
|
||||||
|
// either been imported or type-checked and expanded here, and in either case
|
||||||
|
// its underlying will be fully expanded.
|
||||||
|
for i := 0; i < len(check.defTypes); i++ {
|
||||||
|
n := check.defTypes[i]
|
||||||
|
switch n.underlying.(type) {
|
||||||
|
case nil:
|
||||||
|
if n.resolver == nil {
|
||||||
|
panic("nil underlying")
|
||||||
|
}
|
||||||
|
case *Named:
|
||||||
|
n.under() // n.under may add entries to check.defTypes
|
||||||
|
}
|
||||||
|
n.check = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (check *Checker) record(x *operand) {
|
func (check *Checker) record(x *operand) {
|
||||||
// convert x into a user-friendly set of values
|
// convert x into a user-friendly set of values
|
||||||
// TODO(gri) this code can be simplified
|
// TODO(gri) this code can be simplified
|
||||||
|
@ -65,22 +65,9 @@ func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tpar
|
|||||||
if obj.typ == nil {
|
if obj.typ == nil {
|
||||||
obj.typ = typ
|
obj.typ = typ
|
||||||
}
|
}
|
||||||
// Ensure that typ is always expanded, at which point the check field can be
|
// Ensure that typ is always expanded and sanity-checked.
|
||||||
// nilled out.
|
|
||||||
//
|
|
||||||
// Note that currently we cannot nil out check inside typ.under(), because
|
|
||||||
// it's possible that typ is expanded multiple times.
|
|
||||||
//
|
|
||||||
// TODO(gri): clean this up so that under is the only function mutating
|
|
||||||
// named types.
|
|
||||||
if check != nil {
|
if check != nil {
|
||||||
check.later(func() {
|
check.defTypes = append(check.defTypes, typ)
|
||||||
switch typ.under().(type) {
|
|
||||||
case *Named:
|
|
||||||
panic("unexpanded underlying type")
|
|
||||||
}
|
|
||||||
typ.check = nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return typ
|
return typ
|
||||||
}
|
}
|
||||||
@ -239,6 +226,12 @@ func expandNamed(ctxt *Context, n *Named, instPos syntax.Pos) (tparams *TypePara
|
|||||||
|
|
||||||
check := n.check
|
check := n.check
|
||||||
|
|
||||||
|
if _, unexpanded := n.orig.underlying.(*Named); unexpanded {
|
||||||
|
// We should only get an unexpanded underlying here during type checking
|
||||||
|
// (for example, in recursive type declarations).
|
||||||
|
assert(check != nil)
|
||||||
|
}
|
||||||
|
|
||||||
// Mismatching arg and tparam length may be checked elsewhere.
|
// Mismatching arg and tparam length may be checked elsewhere.
|
||||||
if n.orig.tparams.Len() == n.targs.Len() {
|
if n.orig.tparams.Len() == n.targs.Len() {
|
||||||
// We must always have a context, to avoid infinite recursion.
|
// We must always have a context, to avoid infinite recursion.
|
||||||
|
27
src/cmd/compile/internal/types2/testdata/fixedbugs/issue48703.go2
vendored
Normal file
27
src/cmd/compile/internal/types2/testdata/fixedbugs/issue48703.go2
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2021 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
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
// The actual example from the issue.
|
||||||
|
type List[P any] struct{}
|
||||||
|
|
||||||
|
func (_ List[P]) m() (_ List[List[P]]) { return }
|
||||||
|
|
||||||
|
// Other types of recursion through methods.
|
||||||
|
type R[P any] int
|
||||||
|
|
||||||
|
func (*R[R /* ERROR must be an identifier */ [int]]) m0() {}
|
||||||
|
func (R[P]) m1(R[R[P]]) {}
|
||||||
|
func (R[P]) m2(R[*P]) {}
|
||||||
|
func (R[P]) m3([unsafe.Sizeof(new(R[P]))]int) {}
|
||||||
|
func (R[P]) m4([unsafe.Sizeof(new(R[R[P]]))]int) {}
|
||||||
|
|
||||||
|
// Mutual recursion
|
||||||
|
type M[P any] int
|
||||||
|
|
||||||
|
func (R[P]) m5(M[M[P]]) {}
|
||||||
|
func (M[P]) m(R[R[P]]) {}
|
22
src/cmd/compile/internal/types2/testdata/fixedbugs/issue48974.go2
vendored
Normal file
22
src/cmd/compile/internal/types2/testdata/fixedbugs/issue48974.go2
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2021 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 Fooer interface {
|
||||||
|
Foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fooable[F Fooer] struct {
|
||||||
|
ptr F
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fooable[F]) Adapter() *Fooable[*FooerImpl[F]] {
|
||||||
|
return &Fooable[*FooerImpl[F]]{&FooerImpl[F]{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type FooerImpl[F Fooer] struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fi *FooerImpl[F]) Foo() {}
|
Loading…
Reference in New Issue
Block a user