From: Robert Findley Date: Thu, 23 Dec 2021 15:33:17 +0000 (-0500) Subject: go/types, types2: eagerly check that constraints are not type params X-Git-Tag: go1.18beta2~160 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=7a3a2b18ff3b8591eba18b730da7f84751bbfdc5;p=gostls13.git go/types, types2: eagerly check that constraints are not type params As a result of the change to the underlying of a type parameter to be its constraint interface, we had couple inaccuracies that combined to cause an infinite recursion when type checking the invalid type parameter list [A A]. - We deferred tpar.iface() using check.later twice: once in newTypeParam, and then again at the end of collectTypeParams. - We deferred the check that type parameter constraints are not type parameters, even though this is unnecessary: the constraint type is known. With these inaccuracies, tpar.iface() was executing before our guard against using type parameters as constraints, causing an infinite recursion through under(). Fix this by eagerly checking whether the constraint is a type parameter, and marking it invalid if so. Also remove the unnecessary calls to tpar.iface() at the end of collectTypeParams, as this will already have been scheduled by newTypeParam. Fixes #50321 Change-Id: I4eecbecf21656615867cb94be65b520e9e795bd1 Reviewed-on: https://go-review.googlesource.com/c/go/+/374294 Reviewed-by: Robert Griesemer Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Gopher Robot --- diff --git a/src/cmd/compile/internal/types2/decl.go b/src/cmd/compile/internal/types2/decl.go index d5495304fa..d61d2a8b0d 100644 --- a/src/cmd/compile/internal/types2/decl.go +++ b/src/cmd/compile/internal/types2/decl.go @@ -657,33 +657,24 @@ func (check *Checker) collectTypeParams(dst **TypeParamList, list []*syntax.Fiel // Keep track of bounds for later validation. var bound Type var bounds []Type - var posers []poser for i, f := range list { // Optimization: Re-use the previous type bound if it hasn't changed. // This also preserves the grouped output of type parameter lists // when printing type strings. if i == 0 || f.Type != list[i-1].Type { bound = check.bound(f.Type) - bounds = append(bounds, bound) - posers = append(posers, f.Type) - } - tparams[i].bound = bound - } - - check.later(func() { - for i, bound := range bounds { if isTypeParam(bound) { // We may be able to allow this since it is now well-defined what // the underlying type and thus type set of a type parameter is. // But we may need some additional form of cycle detection within // type parameter lists. - check.error(posers[i], "cannot use a type parameter as constraint") + check.error(f.Type, "cannot use a type parameter as constraint") + bound = Typ[Invalid] } + bounds = append(bounds, bound) } - for _, tpar := range tparams { - tpar.iface() // compute type set - } - }) + tparams[i].bound = bound + } } func (check *Checker) bound(x syntax.Expr) Type { diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50321.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50321.go2 new file mode 100644 index 0000000000..199e66eb6c --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50321.go2 @@ -0,0 +1,8 @@ +// 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 + +func Ln[A A /* ERROR cannot use a type parameter as constraint */ ](p A) { +} diff --git a/src/go/types/decl.go b/src/go/types/decl.go index db29f11920..02af0d5f3e 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -708,34 +708,29 @@ func (check *Checker) collectTypeParams(dst **TypeParamList, list *ast.FieldList index := 0 var bounds []Type - var posns []positioner // bound positions for _, f := range list.List { - // TODO(rfindley) we should be able to rely on f.Type != nil at this point + var bound Type + // NOTE: we may be able to assert that f.Type != nil here, but this is not + // an invariant of the AST, so we are cautious. if f.Type != nil { - bound := check.bound(f.Type) - bounds = append(bounds, bound) - posns = append(posns, f.Type) - for i := range f.Names { - tparams[index+i].bound = bound - } - } - index += len(f.Names) - } - - check.later(func() { - for i, bound := range bounds { + bound = check.bound(f.Type) if isTypeParam(bound) { // We may be able to allow this since it is now well-defined what // the underlying type and thus type set of a type parameter is. // But we may need some additional form of cycle detection within // type parameter lists. - check.error(posns[i], _MisplacedTypeParam, "cannot use a type parameter as constraint") + check.error(f.Type, _MisplacedTypeParam, "cannot use a type parameter as constraint") + bound = Typ[Invalid] } + } else { + bound = Typ[Invalid] } - for _, tpar := range tparams { - tpar.iface() // compute type set + bounds = append(bounds, bound) + for i := range f.Names { + tparams[index+i].bound = bound } - }) + index += len(f.Names) + } } func (check *Checker) bound(x ast.Expr) Type { diff --git a/src/go/types/testdata/fixedbugs/issue50321.go2 b/src/go/types/testdata/fixedbugs/issue50321.go2 new file mode 100644 index 0000000000..199e66eb6c --- /dev/null +++ b/src/go/types/testdata/fixedbugs/issue50321.go2 @@ -0,0 +1,8 @@ +// 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 + +func Ln[A A /* ERROR cannot use a type parameter as constraint */ ](p A) { +}