mirror of
https://github.com/golang/go
synced 2024-09-05 07:54:46 +00:00
[dev.typeparams] cmd/compile/internal/types2: disallow "free" type parameter as RHS of a type declaration
For #45639. Change-Id: I20e331b04f464db81e916af75f70ec8ae73eb989 Reviewed-on: https://go-review.googlesource.com/c/go/+/332411 Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
60cb2cab97
commit
47547d8508
|
@ -626,8 +626,8 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
|
|||
alias = false
|
||||
}
|
||||
|
||||
// alias declaration
|
||||
if alias {
|
||||
// type alias declaration
|
||||
if !check.allowVersion(check.pkg, 1, 9) {
|
||||
if check.conf.CompilerErrorMessages {
|
||||
check.error(tdecl, "type aliases only supported as of -lang=go1.9")
|
||||
|
@ -638,40 +638,44 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
|
|||
|
||||
obj.typ = Typ[Invalid]
|
||||
obj.typ = check.anyType(tdecl.Type)
|
||||
|
||||
} else {
|
||||
// defined type declaration
|
||||
|
||||
named := check.newNamed(obj, nil, nil, nil, nil)
|
||||
def.setUnderlying(named)
|
||||
|
||||
if tdecl.TParamList != nil {
|
||||
check.openScope(tdecl, "type parameters")
|
||||
defer check.closeScope()
|
||||
named.tparams = check.collectTypeParams(tdecl.TParamList)
|
||||
}
|
||||
|
||||
// determine underlying type of named
|
||||
named.fromRHS = check.definedType(tdecl.Type, named)
|
||||
|
||||
// The underlying type of named may be itself a named type that is
|
||||
// incomplete:
|
||||
//
|
||||
// type (
|
||||
// A B
|
||||
// B *C
|
||||
// C A
|
||||
// )
|
||||
//
|
||||
// The type of C is the (named) type of A which is incomplete,
|
||||
// and which has as its underlying type the named type B.
|
||||
// Determine the (final, unnamed) underlying type by resolving
|
||||
// any forward chain.
|
||||
// TODO(gri) Investigate if we can just use named.fromRHS here
|
||||
// and rely on lazy computation of the underlying type.
|
||||
named.underlying = under(named)
|
||||
return
|
||||
}
|
||||
|
||||
// type definition or generic type declaration
|
||||
named := check.newNamed(obj, nil, nil, nil, nil)
|
||||
def.setUnderlying(named)
|
||||
|
||||
if tdecl.TParamList != nil {
|
||||
check.openScope(tdecl, "type parameters")
|
||||
defer check.closeScope()
|
||||
named.tparams = check.collectTypeParams(tdecl.TParamList)
|
||||
}
|
||||
|
||||
// determine underlying type of named
|
||||
named.fromRHS = check.definedType(tdecl.Type, named)
|
||||
|
||||
// The underlying type of named may be itself a named type that is
|
||||
// incomplete:
|
||||
//
|
||||
// type (
|
||||
// A B
|
||||
// B *C
|
||||
// C A
|
||||
// )
|
||||
//
|
||||
// The type of C is the (named) type of A which is incomplete,
|
||||
// and which has as its underlying type the named type B.
|
||||
// Determine the (final, unnamed) underlying type by resolving
|
||||
// any forward chain.
|
||||
// TODO(gri) Investigate if we can just use named.fromRHS here
|
||||
// and rely on lazy computation of the underlying type.
|
||||
named.underlying = under(named)
|
||||
|
||||
// If the RHS is a type parameter, it must be from this type declaration.
|
||||
if tpar, _ := named.underlying.(*TypeParam); tpar != nil && tparamIndex(named.tparams, tpar) < 0 {
|
||||
check.errorf(tdecl.Type, "cannot use function type parameter %s as RHS in type declaration", tpar)
|
||||
named.underlying = Typ[Invalid]
|
||||
}
|
||||
}
|
||||
|
||||
func (check *Checker) collectTypeParams(list []*syntax.Field) []*TypeName {
|
||||
|
|
|
@ -155,30 +155,40 @@ type _ struct {
|
|||
List /* ERROR List redeclared */ [int]
|
||||
}
|
||||
|
||||
// Issue #45639: We don't allow this anymore. Keep this code
|
||||
// in case we decide to revisit this decision.
|
||||
//
|
||||
// It's possible to declare local types whose underlying types
|
||||
// are type parameters. As with ordinary type definitions, the
|
||||
// types underlying properties are "inherited" but the methods
|
||||
// are not.
|
||||
func _[T interface{ m(); ~int }]() {
|
||||
type L T
|
||||
var x L
|
||||
// func _[T interface{ m(); ~int }]() {
|
||||
// type L T
|
||||
// var x L
|
||||
//
|
||||
// // m is not defined on L (it is not "inherited" from
|
||||
// // its underlying type).
|
||||
// x.m /* ERROR x.m undefined */ ()
|
||||
//
|
||||
// // But the properties of T, such that as that it supports
|
||||
// // the operations of the types given by its type bound,
|
||||
// // are also the properties of L.
|
||||
// x++
|
||||
// _ = x - x
|
||||
//
|
||||
// // On the other hand, if we define a local alias for T,
|
||||
// // that alias stands for T as expected.
|
||||
// type A = T
|
||||
// var y A
|
||||
// y.m()
|
||||
// _ = y < 0
|
||||
// }
|
||||
|
||||
// m is not defined on L (it is not "inherited" from
|
||||
// its underlying type).
|
||||
x.m /* ERROR x.m undefined */ ()
|
||||
|
||||
// But the properties of T, such that as that it supports
|
||||
// the operations of the types given by its type bound,
|
||||
// are also the properties of L.
|
||||
x++
|
||||
_ = x - x
|
||||
|
||||
// On the other hand, if we define a local alias for T,
|
||||
// that alias stands for T as expected.
|
||||
type A = T
|
||||
var y A
|
||||
y.m()
|
||||
_ = y < 0
|
||||
// It is not permitted to declare a local type whose underlying
|
||||
// type is a type parameters not declared by that type declaration.
|
||||
func _[T any]() {
|
||||
type _ T // ERROR cannot use function type parameter T as RHS in type declaration
|
||||
type _ [_ any] T // ERROR cannot use function type parameter T as RHS in type declaration
|
||||
}
|
||||
|
||||
// As a special case, an explicit type argument may be omitted
|
||||
|
|
12
src/cmd/compile/internal/types2/testdata/fixedbugs/issue45639.go2
vendored
Normal file
12
src/cmd/compile/internal/types2/testdata/fixedbugs/issue45639.go2
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
// 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
|
||||
|
||||
// It is not permitted to declare a local type whose underlying
|
||||
// type is a type parameters not declared by that type declaration.
|
||||
func _[T any]() {
|
||||
type _ T // ERROR cannot use function type parameter T as RHS in type declaration
|
||||
type _ [_ any] T // ERROR cannot use function type parameter T as RHS in type declaration
|
||||
}
|
|
@ -150,10 +150,17 @@ func (u *unifier) join(i, j int) bool {
|
|||
// If typ is a type parameter of d, index returns the type parameter index.
|
||||
// Otherwise, the result is < 0.
|
||||
func (d *tparamsList) index(typ Type) int {
|
||||
if t, ok := typ.(*TypeParam); ok {
|
||||
if i := t.index; i < len(d.tparams) && d.tparams[i].typ == t {
|
||||
return i
|
||||
}
|
||||
if tpar, ok := typ.(*TypeParam); ok {
|
||||
return tparamIndex(d.tparams, tpar)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// If tpar is a type parameter in list, tparamIndex returns the type parameter index.
|
||||
// Otherwise, the result is < 0. tpar must not be nil.
|
||||
func tparamIndex(list []*TypeName, tpar *TypeParam) int {
|
||||
if i := tpar.index; i < len(list) && list[i].typ == tpar {
|
||||
return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue