]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile/internal/types2: move instantiation code to instantiate...
authorRobert Griesemer <gri@golang.org>
Fri, 9 Jul 2021 20:15:46 +0000 (13:15 -0700)
committerRobert Griesemer <gri@golang.org>
Tue, 13 Jul 2021 04:40:16 +0000 (04:40 +0000)
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 <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/types2/instantiate.go
src/cmd/compile/internal/types2/subst.go

index b289607de6748f26c40ce523a350172b60522074..5ccd511acb080af65c86e11b849cdd8ee9b709a8 100644 (file)
@@ -2,6 +2,9 @@
 // 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 type arguments.
+
 package types2
 
 import (
@@ -9,6 +12,105 @@ import (
        "fmt"
 )
 
+// 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 = &copy
+                       }
+                       // 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)
+}
+
 // InstantiateLazy is like Instantiate, but avoids actually
 // instantiating the type until needed.
 func (check *Checker) InstantiateLazy(pos syntax.Pos, typ Type, targs []Type, verify bool) (res Type) {
@@ -25,3 +127,84 @@ func (check *Checker) InstantiateLazy(pos syntax.Pos, typ Type, targs []Type, ve
                verify: verify,
        }
 }
+
+// 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
+}
index 32cf52737259f4240200d47663c64cd86fe7f663..63b234a60e2253bc2b4818074e5c2ed8e11503f4 100644 (file)
@@ -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 = &copy
-                       }
-                       // 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