mirror of
https://github.com/golang/go
synced 2024-11-11 21:20:21 -07:00
cmd/compile/internal/types2: disallow type cycles through type parameter lists
If we reach a generic type that is part of a cycle and we are in a type parameter list, we have a cycle through a type parameter list, which is invalid. Fixes #49439. Change-Id: Ia6cf97e1748ca0c0e61c02841202050091365b0b Reviewed-on: https://go-review.googlesource.com/c/go/+/361922 Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
318c024b49
commit
cc14fcac2b
@ -46,6 +46,7 @@ type context struct {
|
|||||||
pos syntax.Pos // if valid, identifiers are looked up as if at position pos (used by Eval)
|
pos syntax.Pos // if valid, identifiers are looked up as if at position pos (used by Eval)
|
||||||
iota constant.Value // value of iota in a constant declaration; nil otherwise
|
iota constant.Value // value of iota in a constant declaration; nil otherwise
|
||||||
errpos syntax.Pos // if valid, identifier position of a constant with inherited initializer
|
errpos syntax.Pos // if valid, identifier position of a constant with inherited initializer
|
||||||
|
inTParamList bool // set if inside a type parameter list
|
||||||
sig *Signature // function signature if inside a function; nil otherwise
|
sig *Signature // function signature if inside a function; nil otherwise
|
||||||
isPanic map[*syntax.CallExpr]bool // set of panic call expressions (used for termination check)
|
isPanic map[*syntax.CallExpr]bool // set of panic call expressions (used for termination check)
|
||||||
hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions
|
hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions
|
||||||
|
@ -228,13 +228,23 @@ func (check *Checker) validCycle(obj Object) (valid bool) {
|
|||||||
assert(obj.color() >= grey)
|
assert(obj.color() >= grey)
|
||||||
start := obj.color() - grey // index of obj in objPath
|
start := obj.color() - grey // index of obj in objPath
|
||||||
cycle := check.objPath[start:]
|
cycle := check.objPath[start:]
|
||||||
nval := 0 // number of (constant or variable) values in the cycle
|
tparCycle := false // if set, the cycle is through a type parameter list
|
||||||
ndef := 0 // number of type definitions in the cycle
|
nval := 0 // number of (constant or variable) values in the cycle; valid if !generic
|
||||||
|
ndef := 0 // number of type definitions in the cycle; valid if !generic
|
||||||
|
loop:
|
||||||
for _, obj := range cycle {
|
for _, obj := range cycle {
|
||||||
switch obj := obj.(type) {
|
switch obj := obj.(type) {
|
||||||
case *Const, *Var:
|
case *Const, *Var:
|
||||||
nval++
|
nval++
|
||||||
case *TypeName:
|
case *TypeName:
|
||||||
|
// If we reach a generic type that is part of a cycle
|
||||||
|
// and we are in a type parameter list, we have a cycle
|
||||||
|
// through a type parameter list, which is invalid.
|
||||||
|
if check.inTParamList && isGeneric(obj.typ) {
|
||||||
|
tparCycle = true
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
|
||||||
// Determine if the type name is an alias or not. For
|
// Determine if the type name is an alias or not. For
|
||||||
// package-level objects, use the object map which
|
// package-level objects, use the object map which
|
||||||
// provides syntactic information (which doesn't rely
|
// provides syntactic information (which doesn't rely
|
||||||
@ -262,7 +272,11 @@ func (check *Checker) validCycle(obj Object) (valid bool) {
|
|||||||
|
|
||||||
if check.conf.Trace {
|
if check.conf.Trace {
|
||||||
check.trace(obj.Pos(), "## cycle detected: objPath = %s->%s (len = %d)", pathString(cycle), obj.Name(), len(cycle))
|
check.trace(obj.Pos(), "## cycle detected: objPath = %s->%s (len = %d)", pathString(cycle), obj.Name(), len(cycle))
|
||||||
|
if tparCycle {
|
||||||
|
check.trace(obj.Pos(), "## cycle contains: generic type in a type parameter list")
|
||||||
|
} else {
|
||||||
check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef)
|
check.trace(obj.Pos(), "## cycle contains: %d values, %d type definitions", nval, ndef)
|
||||||
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if !valid {
|
if !valid {
|
||||||
check.trace(obj.Pos(), "=> error: cycle is invalid")
|
check.trace(obj.Pos(), "=> error: cycle is invalid")
|
||||||
@ -270,6 +284,7 @@ func (check *Checker) validCycle(obj Object) (valid bool) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !tparCycle {
|
||||||
// A cycle involving only constants and variables is invalid but we
|
// A cycle involving only constants and variables is invalid but we
|
||||||
// ignore them here because they are reported via the initialization
|
// ignore them here because they are reported via the initialization
|
||||||
// cycle check.
|
// cycle check.
|
||||||
@ -283,6 +298,7 @@ func (check *Checker) validCycle(obj Object) (valid bool) {
|
|||||||
if nval == 0 && ndef > 0 {
|
if nval == 0 && ndef > 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
check.cycleError(cycle)
|
check.cycleError(cycle)
|
||||||
return false
|
return false
|
||||||
@ -624,6 +640,19 @@ func (check *Checker) collectTypeParams(dst **TypeParamList, list []*syntax.Fiel
|
|||||||
// Example: type T[P T[P]] interface{}
|
// Example: type T[P T[P]] interface{}
|
||||||
*dst = bindTParams(tparams)
|
*dst = bindTParams(tparams)
|
||||||
|
|
||||||
|
// Signal to cycle detection that we are in a type parameter list.
|
||||||
|
// We can only be inside one type parameter list at any given time:
|
||||||
|
// function closures may appear inside a type parameter list but they
|
||||||
|
// cannot be generic, and their bodies are processed in delayed and
|
||||||
|
// sequential fashion. Note that with each new declaration, we save
|
||||||
|
// the existing context and restore it when done; thus inTParamList
|
||||||
|
// is true exactly only when we are in a specific type parameter list.
|
||||||
|
assert(!check.inTParamList)
|
||||||
|
check.inTParamList = true
|
||||||
|
defer func() {
|
||||||
|
check.inTParamList = false
|
||||||
|
}()
|
||||||
|
|
||||||
// Keep track of bounds for later validation.
|
// Keep track of bounds for later validation.
|
||||||
var bound Type
|
var bound Type
|
||||||
var bounds []Type
|
var bounds []Type
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
package p
|
package p
|
||||||
|
|
||||||
type Builder[T interface{ struct{ Builder[T] } }] struct{}
|
type Builder /* ERROR illegal cycle */ [T interface{ struct{ Builder[T] } }] struct{}
|
||||||
type myBuilder struct {
|
type myBuilder struct {
|
||||||
Builder[myBuilder /* ERROR myBuilder does not satisfy */]
|
Builder[myBuilder]
|
||||||
}
|
}
|
||||||
|
@ -5,16 +5,16 @@
|
|||||||
package p
|
package p
|
||||||
|
|
||||||
// test case 1
|
// test case 1
|
||||||
type T[U interface{ M() T[U] }] int
|
type T /* ERROR illegal cycle */ [U interface{ M() T[U] }] int
|
||||||
|
|
||||||
type X int
|
type X int
|
||||||
|
|
||||||
func (X) M() T[X] { return 0 }
|
func (X) M() T[X] { return 0 }
|
||||||
|
|
||||||
// test case 2
|
// test case 2
|
||||||
type A[T interface{ A[T] }] interface{}
|
type A /* ERROR illegal cycle */ [T interface{ A[T] }] interface{}
|
||||||
|
|
||||||
// test case 3
|
// test case 3
|
||||||
type A2[U interface{ A2[U] }] interface{ M() A2[U] }
|
type A2 /* ERROR illegal cycle */ [U interface{ A2[U] }] interface{ M() A2[U] }
|
||||||
|
|
||||||
type I interface{ A2[I]; M() A2[I] }
|
type I interface{ A2[I]; M() A2[I] }
|
||||||
|
@ -6,16 +6,16 @@ package p
|
|||||||
|
|
||||||
// parameterized types with self-recursive constraints
|
// parameterized types with self-recursive constraints
|
||||||
type (
|
type (
|
||||||
T1[P T1[P]] interface{}
|
T1 /* ERROR illegal cycle */ [P T1[P]] interface{}
|
||||||
T2[P, Q T2[P, Q]] interface{}
|
T2 /* ERROR illegal cycle */ [P, Q T2[P, Q]] interface{}
|
||||||
T3[P T2[P, Q], Q interface{ ~string }] interface{}
|
T3[P T2[P, Q], Q interface{ ~string }] interface{}
|
||||||
|
|
||||||
T4a[P T4a[P]] interface{ ~int }
|
T4a /* ERROR illegal cycle */ [P T4a[P]] interface{ ~int }
|
||||||
T4b[P T4b[int]] interface{ ~int }
|
T4b /* ERROR illegal cycle */ [P T4b[int]] interface{ ~int }
|
||||||
T4c[P T4c[string /* ERROR string does not satisfy T4c\[string\] */]] interface{ ~int }
|
T4c /* ERROR illegal cycle */ [P T4c[string]] interface{ ~int }
|
||||||
|
|
||||||
// mutually recursive constraints
|
// mutually recursive constraints
|
||||||
T5[P T6[P]] interface{ int }
|
T5 /* ERROR illegal cycle */ [P T6[P]] interface{ int }
|
||||||
T6[P T5[P]] interface{ int }
|
T6[P T5[P]] interface{ int }
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,6 +28,6 @@ var (
|
|||||||
|
|
||||||
// test case from issue
|
// test case from issue
|
||||||
|
|
||||||
type Eq[a Eq[a]] interface {
|
type Eq /* ERROR illegal cycle */ [a Eq[a]] interface {
|
||||||
Equal(that a) bool
|
Equal(that a) bool
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
package p
|
package p
|
||||||
|
|
||||||
type T[U interface{ M() T /* ERROR "got 2 arguments but 1 type parameters" */ [U, int] }] int
|
type T /* ERROR illegal cycle */ [U interface{ M() T[U, int] }] int
|
||||||
|
|
||||||
type X int
|
type X int
|
||||||
|
|
||||||
|
26
src/cmd/compile/internal/types2/testdata/fixedbugs/issue49439.go2
vendored
Normal file
26
src/cmd/compile/internal/types2/testdata/fixedbugs/issue49439.go2
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
type T0 /* ERROR illegal cycle */ [P T0[P]] struct{}
|
||||||
|
|
||||||
|
type T1 /* ERROR illegal cycle */ [P T2[P]] struct{}
|
||||||
|
type T2[P T1[P]] struct{}
|
||||||
|
|
||||||
|
type T3 /* ERROR illegal cycle */ [P interface{ ~struct{ f T3[int] } }] struct{}
|
||||||
|
|
||||||
|
// valid cycle in M
|
||||||
|
type N[P M[P]] struct{}
|
||||||
|
type M[Q any] struct { F *M[Q] }
|
||||||
|
|
||||||
|
// "crazy" case
|
||||||
|
type TC[P [unsafe.Sizeof(func() {
|
||||||
|
type T [P [unsafe.Sizeof(func(){})]byte] struct{}
|
||||||
|
})]byte] struct{}
|
||||||
|
|
||||||
|
// test case from issue
|
||||||
|
type X /* ERROR illegal cycle */ [T any, PT X[T]] interface{}
|
@ -1,4 +1,4 @@
|
|||||||
// compile -G=3
|
// errorcheck -G=3
|
||||||
|
|
||||||
// Copyright 2021 The Go Authors. All rights reserved.
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
package p
|
package p
|
||||||
|
|
||||||
type T[U interface{ M() T[U] }] int
|
type T[U interface{ M() T[U] }] int // ERROR "invalid recursive type T"
|
||||||
|
|
||||||
type X int
|
type X int
|
||||||
|
|
||||||
|
@ -4,4 +4,4 @@
|
|||||||
|
|
||||||
package a
|
package a
|
||||||
|
|
||||||
type T[U interface{ M() T[U] }] int
|
type T[U interface{ M() int }] int
|
||||||
|
@ -8,4 +8,6 @@ import "./a"
|
|||||||
|
|
||||||
type X int
|
type X int
|
||||||
|
|
||||||
func (X) M() a.T[X] { return 0 }
|
func (X) M() int { return 0 }
|
||||||
|
|
||||||
|
type _ a.T[X]
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
package a
|
package a
|
||||||
|
|
||||||
type I[T I[T]] interface {
|
type I[T any] interface {
|
||||||
F() T
|
F() T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,6 @@
|
|||||||
|
|
||||||
package a
|
package a
|
||||||
|
|
||||||
type I[T I[T]] interface {
|
type I[T any] interface {
|
||||||
F() T
|
F() T
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user