mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00:00
cmd/compile/internal/types2: remove asTypeParam and simplify some code
Because we do not permit a stand-alone type parameter on the RHS of a type declaration, the underlying type of a (Named) type cannot be a type parameter. This allows us to simplify some code. Specifically, when parsing union elements, we don't need to delay a check for later, which allows further simplifications when computing type sets. Change-Id: I4047c609f87ebb194ea8c1bad630a70d255b20cf Reviewed-on: https://go-review.googlesource.com/c/go/+/363438 Trust: Robert Griesemer <gri@golang.org> Run-TryBot: Robert Griesemer <gri@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
3634594790
commit
9150c16bce
@ -706,8 +706,7 @@ func (r *importReader) tparamList() []*types2.TypeParam {
|
|||||||
}
|
}
|
||||||
xs := make([]*types2.TypeParam, n)
|
xs := make([]*types2.TypeParam, n)
|
||||||
for i := range xs {
|
for i := range xs {
|
||||||
typ := r.typ()
|
xs[i] = r.typ().(*types2.TypeParam)
|
||||||
xs[i] = types2.AsTypeParam(typ)
|
|
||||||
}
|
}
|
||||||
return xs
|
return xs
|
||||||
}
|
}
|
||||||
|
@ -293,7 +293,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||||||
// the argument types must be of floating-point type
|
// the argument types must be of floating-point type
|
||||||
// (applyTypeFunc never calls f with a type parameter)
|
// (applyTypeFunc never calls f with a type parameter)
|
||||||
f := func(typ Type) Type {
|
f := func(typ Type) Type {
|
||||||
assert(asTypeParam(typ) == nil)
|
assert(!isTypeParam(typ))
|
||||||
if t, _ := under(typ).(*Basic); t != nil {
|
if t, _ := under(typ).(*Basic); t != nil {
|
||||||
switch t.kind {
|
switch t.kind {
|
||||||
case Float32:
|
case Float32:
|
||||||
@ -436,7 +436,7 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
|
|||||||
// the argument must be of complex type
|
// the argument must be of complex type
|
||||||
// (applyTypeFunc never calls f with a type parameter)
|
// (applyTypeFunc never calls f with a type parameter)
|
||||||
f := func(typ Type) Type {
|
f := func(typ Type) Type {
|
||||||
assert(asTypeParam(typ) == nil)
|
assert(!isTypeParam(typ))
|
||||||
if t, _ := under(typ).(*Basic); t != nil {
|
if t, _ := under(typ).(*Basic); t != nil {
|
||||||
switch t.kind {
|
switch t.kind {
|
||||||
case Complex64:
|
case Complex64:
|
||||||
@ -813,7 +813,7 @@ func hasVarSize(t Type) bool {
|
|||||||
// applyTypeFunc returns nil.
|
// applyTypeFunc returns nil.
|
||||||
// If x is not a type parameter, the result is f(x).
|
// If x is not a type parameter, the result is f(x).
|
||||||
func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
|
func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
|
||||||
if tp := asTypeParam(x); tp != nil {
|
if tp, _ := x.(*TypeParam); tp != nil {
|
||||||
// Test if t satisfies the requirements for the argument
|
// Test if t satisfies the requirements for the argument
|
||||||
// type and collect possible result types at the same time.
|
// type and collect possible result types at the same time.
|
||||||
var terms []*Term
|
var terms []*Term
|
||||||
|
@ -528,7 +528,7 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
|
|||||||
check.errorf(e.Sel, "cannot call pointer method %s on %s", sel, x.typ)
|
check.errorf(e.Sel, "cannot call pointer method %s on %s", sel, x.typ)
|
||||||
default:
|
default:
|
||||||
var why string
|
var why string
|
||||||
if tpar := asTypeParam(x.typ); tpar != nil {
|
if tpar, _ := x.typ.(*TypeParam); tpar != nil {
|
||||||
// Type parameter bounds don't specify fields, so don't mention "field".
|
// Type parameter bounds don't specify fields, so don't mention "field".
|
||||||
if tname := tpar.iface().obj; tname != nil {
|
if tname := tpar.iface().obj; tname != nil {
|
||||||
why = check.sprintf("interface %s has no method %s", tname.name, sel)
|
why = check.sprintf("interface %s has no method %s", tname.name, sel)
|
||||||
|
@ -19,12 +19,6 @@ func AsSignature(t Type) *Signature {
|
|||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
// If t is a type parameter, AsTypeParam returns that type, otherwise it returns nil.
|
|
||||||
func AsTypeParam(t Type) *TypeParam {
|
|
||||||
u, _ := t.Underlying().(*TypeParam)
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
|
||||||
// If typ is a type parameter, structuralType returns the single underlying
|
// If typ is a type parameter, structuralType returns the single underlying
|
||||||
// type of all types in the corresponding type constraint if it exists, or
|
// type of all types in the corresponding type constraint if it exists, or
|
||||||
// nil otherwise. If the type set contains only unrestricted and restricted
|
// nil otherwise. If the type set contains only unrestricted and restricted
|
||||||
|
@ -48,7 +48,7 @@ func (check *Checker) conversion(x *operand, T Type) {
|
|||||||
// If T's type set is empty, or if it doesn't
|
// If T's type set is empty, or if it doesn't
|
||||||
// have specific types, constant x cannot be
|
// have specific types, constant x cannot be
|
||||||
// converted.
|
// converted.
|
||||||
ok = under(T).(*TypeParam).underIs(func(u Type) bool {
|
ok = T.(*TypeParam).underIs(func(u Type) bool {
|
||||||
// t is nil if there are no specific type terms
|
// t is nil if there are no specific type terms
|
||||||
if u == nil {
|
if u == nil {
|
||||||
cause = check.sprintf("%s does not contain specific types", T)
|
cause = check.sprintf("%s does not contain specific types", T)
|
||||||
@ -194,8 +194,8 @@ func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// optimization: if we don't have type parameters, we're done
|
// optimization: if we don't have type parameters, we're done
|
||||||
Vp, _ := Vu.(*TypeParam)
|
Vp, _ := V.(*TypeParam)
|
||||||
Tp, _ := Tu.(*TypeParam)
|
Tp, _ := T.(*TypeParam)
|
||||||
if Vp == nil && Tp == nil {
|
if Vp == nil && Tp == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -616,10 +616,11 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *syntax.TypeDecl, def *Named
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Disallow a lone type parameter as the RHS of a type declaration (issue #45639).
|
// Disallow a lone type parameter as the RHS of a type declaration (issue #45639).
|
||||||
// We can look directly at named.underlying because even if it is still a *Named
|
// We don't need this restriction anymore if we make the underlying type of a type
|
||||||
// type (underlying not fully resolved yet) it cannot become a type parameter due
|
// parameter its constraint interface: if the RHS is a lone type parameter, we will
|
||||||
// to this very restriction.
|
// use its underlying type (like we do for any RHS in a type declaration), and its
|
||||||
if tpar, _ := named.underlying.(*TypeParam); tpar != nil {
|
// underlying type is an interface and the type declaration is well defined.
|
||||||
|
if isTypeParam(rhs) {
|
||||||
check.error(tdecl.Type, "cannot use a type parameter as RHS in type declaration")
|
check.error(tdecl.Type, "cannot use a type parameter as RHS in type declaration")
|
||||||
named.underlying = Typ[Invalid]
|
named.underlying = Typ[Invalid]
|
||||||
}
|
}
|
||||||
@ -671,7 +672,7 @@ func (check *Checker) collectTypeParams(dst **TypeParamList, list []*syntax.Fiel
|
|||||||
|
|
||||||
check.later(func() {
|
check.later(func() {
|
||||||
for i, bound := range bounds {
|
for i, bound := range bounds {
|
||||||
if _, ok := under(bound).(*TypeParam); ok {
|
if isTypeParam(bound) {
|
||||||
check.error(posers[i], "cannot use a type parameter as constraint")
|
check.error(posers[i], "cannot use a type parameter as constraint")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,11 +160,10 @@ var op2str2 = [...]string{
|
|||||||
// If typ is a type parameter, underIs returns the result of typ.underIs(f).
|
// If typ is a type parameter, underIs returns the result of typ.underIs(f).
|
||||||
// Otherwise, underIs returns the result of f(under(typ)).
|
// Otherwise, underIs returns the result of f(under(typ)).
|
||||||
func underIs(typ Type, f func(Type) bool) bool {
|
func underIs(typ Type, f func(Type) bool) bool {
|
||||||
u := under(typ)
|
if tpar, _ := typ.(*TypeParam); tpar != nil {
|
||||||
if tpar, _ := u.(*TypeParam); tpar != nil {
|
|
||||||
return tpar.underIs(f)
|
return tpar.underIs(f)
|
||||||
}
|
}
|
||||||
return f(u)
|
return f(under(typ))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *Checker) unary(x *operand, e *syntax.Operation) {
|
func (check *Checker) unary(x *operand, e *syntax.Operation) {
|
||||||
|
@ -143,7 +143,7 @@ func (check *Checker) satisfies(pos syntax.Pos, targ Type, tpar *TypeParam, smap
|
|||||||
|
|
||||||
// A type argument that is a type parameter with an empty type set satisfies any constraint.
|
// A type argument that is a type parameter with an empty type set satisfies any constraint.
|
||||||
// (The empty set is a subset of any set.)
|
// (The empty set is a subset of any set.)
|
||||||
if targ := asTypeParam(targ); targ != nil && targ.iface().typeSet().IsEmpty() {
|
if targ, _ := targ.(*TypeParam); targ != nil && targ.iface().typeSet().IsEmpty() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +172,7 @@ func (check *Checker) satisfies(pos syntax.Pos, targ Type, tpar *TypeParam, smap
|
|||||||
// if iface is comparable, targ must be comparable
|
// if iface is comparable, targ must be comparable
|
||||||
// TODO(gri) the error messages needs to be better, here
|
// TODO(gri) the error messages needs to be better, here
|
||||||
if iface.IsComparable() && !Comparable(targ) {
|
if iface.IsComparable() && !Comparable(targ) {
|
||||||
if tpar := asTypeParam(targ); tpar != nil && tpar.iface().typeSet().IsAll() {
|
if tpar, _ := targ.(*TypeParam); tpar != nil && tpar.iface().typeSet().IsAll() {
|
||||||
return errorf("%s has no constraints", targ)
|
return errorf("%s has no constraints", targ)
|
||||||
}
|
}
|
||||||
return errorf("%s does not satisfy comparable", targ)
|
return errorf("%s does not satisfy comparable", targ)
|
||||||
@ -184,7 +184,7 @@ func (check *Checker) satisfies(pos syntax.Pos, targ Type, tpar *TypeParam, smap
|
|||||||
// If the type argument is a pointer to a type parameter, the type argument's
|
// If the type argument is a pointer to a type parameter, the type argument's
|
||||||
// method set is empty.
|
// method set is empty.
|
||||||
// TODO(gri) is this what we want? (spec question)
|
// TODO(gri) is this what we want? (spec question)
|
||||||
if base, isPtr := deref(targ); isPtr && asTypeParam(base) != nil {
|
if base, isPtr := deref(targ); isPtr && isTypeParam(base) {
|
||||||
return errorf("%s has no methods", targ)
|
return errorf("%s has no methods", targ)
|
||||||
}
|
}
|
||||||
if m, wrong := check.missingMethod(targ, iface, true); m != nil {
|
if m, wrong := check.missingMethod(targ, iface, true); m != nil {
|
||||||
@ -212,7 +212,7 @@ func (check *Checker) satisfies(pos syntax.Pos, targ Type, tpar *TypeParam, smap
|
|||||||
// If targ is itself a type parameter, each of its possible types must be in the set
|
// If targ is itself a type parameter, each of its possible types must be in the set
|
||||||
// of iface types (i.e., the targ type set must be a subset of the iface type set).
|
// of iface types (i.e., the targ type set must be a subset of the iface type set).
|
||||||
// Type arguments with empty type sets were already excluded above.
|
// Type arguments with empty type sets were already excluded above.
|
||||||
if targ := asTypeParam(targ); targ != nil {
|
if targ, _ := targ.(*TypeParam); targ != nil {
|
||||||
targBound := targ.iface()
|
targBound := targ.iface()
|
||||||
if !targBound.typeSet().subsetOf(iface.typeSet()) {
|
if !targBound.typeSet().subsetOf(iface.typeSet()) {
|
||||||
// TODO(gri) report which type is missing
|
// TODO(gri) report which type is missing
|
||||||
|
@ -134,12 +134,8 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
|
|||||||
continue // we can't have a matching field or interface method
|
continue // we can't have a matching field or interface method
|
||||||
}
|
}
|
||||||
|
|
||||||
// continue with underlying type, but only if it's not a type parameter
|
// continue with underlying type
|
||||||
// TODO(gri) is this what we want to do for type parameters? (spec question)
|
|
||||||
typ = named.under()
|
typ = named.under()
|
||||||
if asTypeParam(typ) != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tpar = nil
|
tpar = nil
|
||||||
|
@ -183,7 +183,7 @@ func operandString(x *operand, qf Qualifier) string {
|
|||||||
}
|
}
|
||||||
buf.WriteString(intro)
|
buf.WriteString(intro)
|
||||||
WriteType(&buf, x.typ, qf)
|
WriteType(&buf, x.typ, qf)
|
||||||
if tpar := asTypeParam(x.typ); tpar != nil {
|
if tpar, _ := x.typ.(*TypeParam); tpar != nil {
|
||||||
buf.WriteString(" constrained by ")
|
buf.WriteString(" constrained by ")
|
||||||
WriteType(&buf, tpar.bound, qf) // do not compute interface type sets here
|
WriteType(&buf, tpar.bound, qf) // do not compute interface type sets here
|
||||||
}
|
}
|
||||||
@ -256,8 +256,8 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
|
|||||||
|
|
||||||
Vu := under(V)
|
Vu := under(V)
|
||||||
Tu := under(T)
|
Tu := under(T)
|
||||||
Vp, _ := Vu.(*TypeParam)
|
Vp, _ := V.(*TypeParam)
|
||||||
Tp, _ := Tu.(*TypeParam)
|
Tp, _ := T.(*TypeParam)
|
||||||
|
|
||||||
// x is an untyped value representable by a value of type T.
|
// x is an untyped value representable by a value of type T.
|
||||||
if isUntyped(Vu) {
|
if isUntyped(Vu) {
|
||||||
|
@ -90,7 +90,7 @@ func IsInterface(t Type) bool {
|
|||||||
|
|
||||||
// isTypeParam reports whether t is a type parameter.
|
// isTypeParam reports whether t is a type parameter.
|
||||||
func isTypeParam(t Type) bool {
|
func isTypeParam(t Type) bool {
|
||||||
_, ok := under(t).(*TypeParam)
|
_, ok := t.(*TypeParam)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,9 +88,3 @@ func asNamed(t Type) *Named {
|
|||||||
}
|
}
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
// If t is a type parameter, asTypeParam returns that type, otherwise it returns nil.
|
|
||||||
func asTypeParam(t Type) *TypeParam {
|
|
||||||
u, _ := under(t).(*TypeParam)
|
|
||||||
return u
|
|
||||||
}
|
|
||||||
|
@ -291,10 +291,6 @@ func computeInterfaceTypeSet(check *Checker, pos syntax.Pos, ityp *Interface) *_
|
|||||||
continue // ignore invalid unions
|
continue // ignore invalid unions
|
||||||
}
|
}
|
||||||
terms = tset.terms
|
terms = tset.terms
|
||||||
case *TypeParam:
|
|
||||||
// Embedding stand-alone type parameters is not permitted.
|
|
||||||
// Union parsing reports a (delayed) error, so we can ignore this entry.
|
|
||||||
continue
|
|
||||||
default:
|
default:
|
||||||
if u == Typ[Invalid] {
|
if u == Typ[Invalid] {
|
||||||
continue
|
continue
|
||||||
@ -372,10 +368,6 @@ func computeUnionTypeSet(check *Checker, pos syntax.Pos, utyp *Union) *_TypeSet
|
|||||||
switch u := under(t.typ).(type) {
|
switch u := under(t.typ).(type) {
|
||||||
case *Interface:
|
case *Interface:
|
||||||
terms = computeInterfaceTypeSet(check, pos, u).terms
|
terms = computeInterfaceTypeSet(check, pos, u).terms
|
||||||
case *TypeParam:
|
|
||||||
// A stand-alone type parameters is not permitted as union term.
|
|
||||||
// Union parsing reports a (delayed) error, so we can ignore this entry.
|
|
||||||
continue
|
|
||||||
default:
|
default:
|
||||||
if t.typ == Typ[Invalid] {
|
if t.typ == Typ[Invalid] {
|
||||||
continue
|
continue
|
||||||
|
@ -356,7 +356,7 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *Named) (T Type) {
|
|||||||
check.later(func() {
|
check.later(func() {
|
||||||
if !Comparable(typ.key) {
|
if !Comparable(typ.key) {
|
||||||
var why string
|
var why string
|
||||||
if asTypeParam(typ.key) != nil {
|
if isTypeParam(typ.key) {
|
||||||
why = " (missing comparable constraint)"
|
why = " (missing comparable constraint)"
|
||||||
}
|
}
|
||||||
check.errorf(e.Key, "invalid map key type %s%s", typ.key, why)
|
check.errorf(e.Key, "invalid map key type %s%s", typ.key, why)
|
||||||
|
@ -115,15 +115,14 @@ func parseTilde(check *Checker, x syntax.Expr) (tilde bool, typ Type) {
|
|||||||
}
|
}
|
||||||
typ = check.typ(x)
|
typ = check.typ(x)
|
||||||
// Embedding stand-alone type parameters is not permitted (issue #47127).
|
// Embedding stand-alone type parameters is not permitted (issue #47127).
|
||||||
// Do this check later because it requires computation of the underlying type (see also issue #46461).
|
// We don't need this restriction anymore if we make the underlying type of a type
|
||||||
// Note: If an underlying type cannot be a type parameter, the call to
|
// parameter its constraint interface: if we embed a lone type parameter, we will
|
||||||
// under() will not be needed and then we don't need to delay this
|
// simply use its underlying type (like we do for other named, embedded interfaces),
|
||||||
// check to later and could return Typ[Invalid] instead.
|
// and since the underlying type is an interface the embedding is well defined.
|
||||||
check.later(func() {
|
if isTypeParam(typ) {
|
||||||
if _, ok := under(typ).(*TypeParam); ok {
|
|
||||||
check.error(x, "cannot embed a type parameter")
|
check.error(x, "cannot embed a type parameter")
|
||||||
|
typ = Typ[Invalid]
|
||||||
}
|
}
|
||||||
})
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user