From 47547d8508ab416e28992e0e0965c9c25f840848 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 1 Jul 2021 22:10:01 -0700 Subject: [PATCH] [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 Reviewed-by: Robert Findley --- src/cmd/compile/internal/types2/decl.go | 70 ++++++++++--------- .../types2/testdata/examples/types.go2 | 48 ++++++++----- .../types2/testdata/fixedbugs/issue45639.go2 | 12 ++++ src/cmd/compile/internal/types2/unify.go | 15 ++-- 4 files changed, 89 insertions(+), 56 deletions(-) create mode 100644 src/cmd/compile/internal/types2/testdata/fixedbugs/issue45639.go2 diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index d36da06f42..4f91bc70c7 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -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 { diff --git a/src/cmd/compile/internal/types2/testdata/examples/types.go2 b/src/cmd/compile/internal/types2/testdata/examples/types.go2 index 66e7a7b90e..4ecc34dfa4 100644 --- a/src/cmd/compile/internal/types2/testdata/examples/types.go2 +++ b/src/cmd/compile/internal/types2/testdata/examples/types.go2 @@ -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 diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45639.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45639.go2 new file mode 100644 index 0000000000..441fb4cb34 --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue45639.go2 @@ -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 +} diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go index 4e1f832203..755622738a 100644 --- a/src/cmd/compile/internal/types2/unify.go +++ b/src/cmd/compile/internal/types2/unify.go @@ -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 }