From 70f1246a9f861bdfe2ea81db0f1545bd31ff6d49 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Fri, 9 Jul 2021 13:15:46 -0700 Subject: [dev.typeparams] cmd/compile/internal/types2: move instantiation code to instantiate.go (cleanup) No code changes besides moving the two functions and updating a couple of file comments. Change-Id: I13a6a78b6e8c132c20c7f81a329f31d5edab0453 Reviewed-on: https://go-review.googlesource.com/c/go/+/333589 Trust: Robert Griesemer Reviewed-by: Robert Findley --- src/cmd/compile/internal/types2/subst.go | 184 +------------------------------ 1 file changed, 1 insertion(+), 183 deletions(-) (limited to 'src/cmd/compile/internal/types2/subst.go') diff --git a/src/cmd/compile/internal/types2/subst.go b/src/cmd/compile/internal/types2/subst.go index 32cf527372..63b234a60e 100644 --- a/src/cmd/compile/internal/types2/subst.go +++ b/src/cmd/compile/internal/types2/subst.go @@ -2,9 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file implements instantiation of generic types -// through substitution of type parameters by actual -// types. +// This file implements type parameter substitution. package types2 @@ -53,186 +51,6 @@ func (m *substMap) lookup(tpar *TypeParam) Type { return tpar } -// Instantiate instantiates the type typ with the given type arguments -// targs. To check type constraint satisfaction, verify must be set. -// pos and posList correspond to the instantiation and type argument -// positions respectively; posList may be nil or shorter than the number -// of type arguments provided. -// typ must be a *Named or a *Signature type, and its number of type -// parameters must match the number of provided type arguments. -// The receiver (check) may be nil if and only if verify is not set. -// The result is a new, instantiated (not generic) type of the same kind -// (either a *Named or a *Signature). -// Any methods attached to a *Named are simply copied; they are not -// instantiated. -func (check *Checker) Instantiate(pos syntax.Pos, typ Type, targs []Type, posList []syntax.Pos, verify bool) (res Type) { - if verify && check == nil { - panic("cannot have nil receiver if verify is set") - } - - if check != nil && check.conf.Trace { - check.trace(pos, "-- instantiating %s with %s", typ, typeListString(targs)) - check.indent++ - defer func() { - check.indent-- - var under Type - if res != nil { - // Calling under() here may lead to endless instantiations. - // Test case: type T[P any] T[P] - // TODO(gri) investigate if that's a bug or to be expected. - under = res.Underlying() - } - check.trace(pos, "=> %s (under = %s)", res, under) - }() - } - - assert(len(posList) <= len(targs)) - - // TODO(gri) What is better here: work with TypeParams, or work with TypeNames? - var tparams []*TypeName - switch t := typ.(type) { - case *Named: - tparams = t.TParams() - case *Signature: - tparams = t.tparams - defer func() { - // If we had an unexpected failure somewhere don't panic below when - // asserting res.(*Signature). Check for *Signature in case Typ[Invalid] - // is returned. - if _, ok := res.(*Signature); !ok { - return - } - // If the signature doesn't use its type parameters, subst - // will not make a copy. In that case, make a copy now (so - // we can set tparams to nil w/o causing side-effects). - if t == res { - copy := *t - res = © - } - // After instantiating a generic signature, it is not generic - // anymore; we need to set tparams to nil. - res.(*Signature).tparams = nil - }() - default: - // only types and functions can be generic - panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ)) - } - - // the number of supplied types must match the number of type parameters - if len(targs) != len(tparams) { - // TODO(gri) provide better error message - if check != nil { - check.errorf(pos, "got %d arguments but %d type parameters", len(targs), len(tparams)) - return Typ[Invalid] - } - panic(fmt.Sprintf("%v: got %d arguments but %d type parameters", pos, len(targs), len(tparams))) - } - - if len(tparams) == 0 { - return typ // nothing to do (minor optimization) - } - - smap := makeSubstMap(tparams, targs) - - // check bounds - if verify { - for i, tname := range tparams { - // best position for error reporting - pos := pos - if i < len(posList) { - pos = posList[i] - } - // stop checking bounds after the first failure - if !check.satisfies(pos, targs[i], tname.typ.(*TypeParam), smap) { - break - } - } - } - - return check.subst(pos, typ, smap) -} - -// satisfies reports whether the type argument targ satisfies the constraint of type parameter -// parameter tpar (after any of its type parameters have been substituted through smap). -// A suitable error is reported if the result is false. -// TODO(gri) This should be a method of interfaces or type sets. -func (check *Checker) satisfies(pos syntax.Pos, targ Type, tpar *TypeParam, smap *substMap) bool { - iface := tpar.Bound() - if iface.Empty() { - return true // no type bound - } - - // The type parameter bound is parameterized with the same type parameters - // as the instantiated type; before we can use it for bounds checking we - // need to instantiate it with the type arguments with which we instantiate - // the parameterized type. - iface = check.subst(pos, iface, smap).(*Interface) - - // targ must implement iface (methods) - // - check only if we have methods - if iface.NumMethods() > 0 { - // 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 { - check.errorf(pos, "%s has no methods", targ) - return false - } - if m, wrong := check.missingMethod(targ, iface, true); m != nil { - // TODO(gri) needs to print updated name to avoid major confusion in error message! - // (print warning for now) - // Old warning: - // check.softErrorf(pos, "%s does not satisfy %s (warning: name not updated) = %s (missing method %s)", targ, tpar.bound, iface, m) - if m.name == "==" { - // We don't want to report "missing method ==". - check.softErrorf(pos, "%s does not satisfy comparable", targ) - } else if wrong != nil { - // TODO(gri) This can still report uninstantiated types which makes the error message - // more difficult to read then necessary. - check.softErrorf(pos, - "%s does not satisfy %s: wrong method signature\n\tgot %s\n\twant %s", - targ, tpar.bound, wrong, m, - ) - } else { - check.softErrorf(pos, "%s does not satisfy %s (missing method %s)", targ, tpar.bound, m.name) - } - return false - } - } - - // targ's underlying type must also be one of the interface types listed, if any - if iface.typeSet().types == nil { - return true // nothing to do - } - - // If targ is itself a type parameter, each of its possible types, but at least one, must be in the - // list of iface types (i.e., the targ type list must be a non-empty subset of the iface types). - if targ := asTypeParam(targ); targ != nil { - targBound := targ.Bound() - if targBound.typeSet().types == nil { - check.softErrorf(pos, "%s does not satisfy %s (%s has no type constraints)", targ, tpar.bound, targ) - return false - } - return iface.is(func(typ Type, tilde bool) bool { - // TODO(gri) incorporate tilde information! - if !iface.isSatisfiedBy(typ) { - // TODO(gri) match this error message with the one below (or vice versa) - check.softErrorf(pos, "%s does not satisfy %s (%s type constraint %s not found in %s)", targ, tpar.bound, targ, typ, iface.typeSet().types) - return false - } - return true - }) - } - - // Otherwise, targ's type or underlying type must also be one of the interface types listed, if any. - if !iface.isSatisfiedBy(targ) { - check.softErrorf(pos, "%s does not satisfy %s (%s not found in %s)", targ, tpar.bound, targ, iface.typeSet().types) - return false - } - - return true -} - // subst returns the type typ with its type parameters tpars replaced by // the corresponding type arguments targs, recursively. // subst is functional in the sense that it doesn't modify the incoming -- cgit v1.2.3-54-g00ecf