From: Robert Findley Date: Tue, 16 Nov 2021 04:39:22 +0000 (-0500) Subject: go/types: remove asTypeParam and simplify some code X-Git-Tag: go1.18beta1~294 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=7c50ef6c8c4c827db45a3327cb950913cf9d489b;p=gostls13.git go/types: remove asTypeParam and simplify some code This is a port of CL 363438 from types2 to go/types. Change-Id: I87c76d31b398b9ce406f96b0030ee458619b3dbe Reviewed-on: https://go-review.googlesource.com/c/go/+/364235 Trust: Robert Findley Run-TryBot: Robert Findley TryBot-Result: Go Bot Reviewed-by: Robert Griesemer --- diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index c1932232aa..5418d66aeb 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -298,7 +298,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b // the argument types must be of floating-point type // (applyTypeFunc never calls f with a type parameter) f := func(typ Type) Type { - assert(asTypeParam(typ) == nil) + assert(!isTypeParam(typ)) if t, _ := under(typ).(*Basic); t != nil { switch t.kind { case Float32: @@ -441,7 +441,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b // the argument must be of complex type // (applyTypeFunc never calls f with a type parameter) f := func(typ Type) Type { - assert(asTypeParam(typ) == nil) + assert(!isTypeParam(typ)) if t, _ := under(typ).(*Basic); t != nil { switch t.kind { case Complex64: @@ -822,7 +822,7 @@ func hasVarSize(t Type) bool { // applyTypeFunc returns nil. // If x is not a type parameter, the result is f(x). 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 // type and collect possible result types at the same time. var terms []*Term diff --git a/src/go/types/call.go b/src/go/types/call.go index dfd7142094..7cb6027f3b 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -531,7 +531,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) { check.errorf(e.Sel, _InvalidMethodExpr, "cannot call pointer method %s on %s", sel, x.typ) default: 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". if tname := tpar.iface().obj; tname != nil { why = check.sprintf("interface %s has no method %s", tname.name, sel) diff --git a/src/go/types/conversions.go b/src/go/types/conversions.go index 18d24e404c..eadc923f5e 100644 --- a/src/go/types/conversions.go +++ b/src/go/types/conversions.go @@ -47,7 +47,7 @@ func (check *Checker) conversion(x *operand, T Type) { // If T's type set is empty, or if it doesn't // have specific types, constant x cannot be // 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 if u == nil { cause = check.sprintf("%s does not contain specific types", T) @@ -186,8 +186,8 @@ func (x *operand) convertibleTo(check *Checker, T Type, cause *string) bool { } // optimization: if we don't have type parameters, we're done - Vp, _ := Vu.(*TypeParam) - Tp, _ := Tu.(*TypeParam) + Vp, _ := V.(*TypeParam) + Tp, _ := T.(*TypeParam) if Vp == nil && Tp == nil { return false } diff --git a/src/go/types/decl.go b/src/go/types/decl.go index e12961416e..2108cf6b05 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -669,10 +669,11 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *Named) { } // 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 - // type (underlying not fully resolved yet) it cannot become a type parameter due - // to this very restriction. - if tpar, _ := named.underlying.(*TypeParam); tpar != nil { + // We don't need this restriction anymore if we make the underlying type of a type + // parameter its constraint interface: if the RHS is a lone type parameter, we will + // use its underlying type (like we do for any RHS in a type declaration), and its + // underlying type is an interface and the type declaration is well defined. + if isTypeParam(rhs) { check.error(tdecl.Type, _MisplacedTypeParam, "cannot use a type parameter as RHS in type declaration") named.underlying = Typ[Invalid] } @@ -723,7 +724,7 @@ func (check *Checker) collectTypeParams(dst **TypeParamList, list *ast.FieldList check.later(func() { for i, bound := range bounds { - if _, ok := under(bound).(*TypeParam); ok { + if isTypeParam(bound) { check.error(posns[i], _MisplacedTypeParam, "cannot use a type parameter as constraint") } } diff --git a/src/go/types/expr.go b/src/go/types/expr.go index 6eeb431b73..660c92de3b 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -147,11 +147,10 @@ var op2str2 = [...]string{ // If typ is a type parameter, underIs returns the result of typ.underIs(f). // Otherwise, underIs returns the result of f(under(typ)). func underIs(typ Type, f func(Type) bool) bool { - u := under(typ) - if tpar, _ := u.(*TypeParam); tpar != nil { + if tpar, _ := typ.(*TypeParam); tpar != nil { return tpar.underIs(f) } - return f(u) + return f(under(typ)) } // The unary expression e may be nil. It's passed in for better error messages only. diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index 13d6e3114d..c9ce6f6ae1 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -157,7 +157,7 @@ func (check *Checker) satisfies(pos token.Pos, targ Type, tpar *TypeParam, smap // 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.) - if targ := asTypeParam(targ); targ != nil && targ.iface().typeSet().IsEmpty() { + if targ, _ := targ.(*TypeParam); targ != nil && targ.iface().typeSet().IsEmpty() { return nil } @@ -186,7 +186,7 @@ func (check *Checker) satisfies(pos token.Pos, targ Type, tpar *TypeParam, smap // if iface is comparable, targ must be comparable // TODO(gri) the error messages needs to be better, here 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 does not satisfy comparable", targ) @@ -198,7 +198,7 @@ func (check *Checker) satisfies(pos token.Pos, targ Type, tpar *TypeParam, smap // If the type argument is a pointer to a type parameter, the type argument's // method set is empty. // 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) } if m, wrong := check.missingMethod(targ, iface, true); m != nil { @@ -227,7 +227,7 @@ func (check *Checker) satisfies(pos token.Pos, targ Type, tpar *TypeParam, smap // 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). // 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() if !targBound.typeSet().subsetOf(iface.typeSet()) { // TODO(gri) report which type is missing diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index aae6fa206d..98af6bfcd7 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -134,14 +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 with underlying type, but only if it's not a type parameter - // TODO(gri) is this what we want to do for type parameters? (spec question) - // TODO(#45639) the error message produced as a result of skipping an - // underlying type parameter should be improved. + // continue with underlying type typ = named.under() - if asTypeParam(typ) != nil { - continue - } } tpar = nil diff --git a/src/go/types/operand.go b/src/go/types/operand.go index 8b76e939b6..6f902e9749 100644 --- a/src/go/types/operand.go +++ b/src/go/types/operand.go @@ -166,7 +166,7 @@ func operandString(x *operand, qf Qualifier) string { } buf.WriteString(intro) WriteType(&buf, x.typ, qf) - if tpar := asTypeParam(x.typ); tpar != nil { + if tpar, _ := x.typ.(*TypeParam); tpar != nil { buf.WriteString(" constrained by ") WriteType(&buf, tpar.bound, qf) // do not compute interface type sets here } @@ -241,8 +241,8 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er Vu := under(V) Tu := under(T) - Vp, _ := Vu.(*TypeParam) - Tp, _ := Tu.(*TypeParam) + Vp, _ := V.(*TypeParam) + Tp, _ := T.(*TypeParam) // x is an untyped value representable by a value of type T. if isUntyped(Vu) { diff --git a/src/go/types/predicates.go b/src/go/types/predicates.go index 2d9b9c4c07..d0697b1ad7 100644 --- a/src/go/types/predicates.go +++ b/src/go/types/predicates.go @@ -92,7 +92,7 @@ func IsInterface(t Type) bool { // isTypeParam reports whether t is a type parameter. func isTypeParam(t Type) bool { - _, ok := under(t).(*TypeParam) + _, ok := t.(*TypeParam) return ok } diff --git a/src/go/types/type.go b/src/go/types/type.go index e26d8189d1..555eb9e8b9 100644 --- a/src/go/types/type.go +++ b/src/go/types/type.go @@ -88,9 +88,3 @@ func asNamed(t Type) *Named { } 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 -} diff --git a/src/go/types/typeset.go b/src/go/types/typeset.go index 1e6b9dd390..d0464aeaa0 100644 --- a/src/go/types/typeset.go +++ b/src/go/types/typeset.go @@ -289,10 +289,6 @@ func computeInterfaceTypeSet(check *Checker, pos token.Pos, ityp *Interface) *_T continue // ignore invalid unions } 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: if u == Typ[Invalid] { continue @@ -370,10 +366,6 @@ func computeUnionTypeSet(check *Checker, pos token.Pos, utyp *Union) *_TypeSet { switch u := under(t.typ).(type) { case *Interface: 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: if t.typ == Typ[Invalid] { continue diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 5828c2e7c3..89264ee9eb 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -337,7 +337,7 @@ func (check *Checker) typInternal(e0 ast.Expr, def *Named) (T Type) { check.later(func() { if !Comparable(typ.key) { var why string - if asTypeParam(typ.key) != nil { + if isTypeParam(typ.key) { why = " (missing comparable constraint)" } check.errorf(e.Key, _IncomparableMapKey, "incomparable map key type %s%s", typ.key, why) diff --git a/src/go/types/union.go b/src/go/types/union.go index bb08174728..2a65ca4d8e 100644 --- a/src/go/types/union.go +++ b/src/go/types/union.go @@ -118,15 +118,14 @@ func parseTilde(check *Checker, x ast.Expr) (tilde bool, typ Type) { } typ = check.typ(x) // 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). - // Note: If an underlying type cannot be a type parameter, the call to - // under() will not be needed and then we don't need to delay this - // check to later and could return Typ[Invalid] instead. - check.later(func() { - if _, ok := under(typ).(*TypeParam); ok { - check.error(x, _MisplacedTypeParam, "cannot embed a type parameter") - } - }) + // We don't need this restriction anymore if we make the underlying type of a type + // parameter its constraint interface: if we embed a lone type parameter, we will + // simply use its underlying type (like we do for other named, embedded interfaces), + // and since the underlying type is an interface the embedding is well defined. + if isTypeParam(typ) { + check.error(x, _MisplacedTypeParam, "cannot embed a type parameter") + typ = Typ[Invalid] + } return }