]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile/internal/types2: replace types2.Instantiate with Checker...
authorRobert Griesemer <gri@golang.org>
Fri, 9 Jul 2021 20:02:24 +0000 (13:02 -0700)
committerRobert Griesemer <gri@golang.org>
Tue, 13 Jul 2021 04:40:11 +0000 (04:40 +0000)
Allow Checker.Instantiate to work with a nil *Checker receiver
(for now). This opens the door to passing in a *Checker at all
times.

Also, added a verify flag to Instantiate, InstantiateLazy, and
instance, to be able to control if constraint satisfaction should
be checked or not.

Removed types2.Instantiate.

For #47103.

Change-Id: Ie00ce41b3e50a0fc4341e013922e5f874276d282
Reviewed-on: https://go-review.googlesource.com/c/go/+/333569
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
src/cmd/compile/internal/importer/iimport.go
src/cmd/compile/internal/noder/reader2.go
src/cmd/compile/internal/types2/api_test.go
src/cmd/compile/internal/types2/call.go
src/cmd/compile/internal/types2/instance.go
src/cmd/compile/internal/types2/instantiate.go
src/cmd/compile/internal/types2/sizeof_test.go
src/cmd/compile/internal/types2/subst.go
src/cmd/compile/internal/types2/typexpr.go

index 14e64891b8016f1cdbf9eeb03e8b1356df6ef4de..453fa40f2d6c2a58fedf9d829fe3f25c5a1eed63 100644 (file)
@@ -665,7 +665,8 @@ func (r *importReader) doType(base *types2.Named) types2.Type {
                baseType := r.typ()
                // The imported instantiated type doesn't include any methods, so
                // we must always use the methods of the base (orig) type.
-               t := types2.Instantiate(pos, baseType, targs)
+               var check *types2.Checker // TODO provide a non-nil *Checker
+               t := check.Instantiate(pos, baseType, targs, nil, false)
                return t
 
        case unionType:
index 89f224d389c8e924ae98484ea561c0ea18d8202b..92569ff843f87df5ddcb3891079aaca987346a17 100644 (file)
@@ -224,7 +224,7 @@ func (r *reader2) doTyp() (res types2.Type) {
                obj, targs := r.obj()
                name := obj.(*types2.TypeName)
                if len(targs) != 0 {
-                       return r.p.check.InstantiateLazy(syntax.Pos{}, name.Type(), targs)
+                       return r.p.check.InstantiateLazy(syntax.Pos{}, name.Type(), targs, false)
                }
                return name.Type()
 
index 1c535387d403832451763a6f3a2b00d6c390d406..74e3da3fe13699f4c0a74994ebf7a3110e0774e3 100644 (file)
@@ -1862,7 +1862,9 @@ func TestInstantiate(t *testing.T) {
        }
 
        // instantiation should succeed (no endless recursion)
-       res := Instantiate(nopos, T, []Type{Typ[Int]})
+       // even with a nil *Checker
+       var check *Checker
+       res := check.Instantiate(nopos, T, []Type{Typ[Int]}, nil, false)
 
        // instantiated type should point to itself
        if res.Underlying().(*Pointer).Elem() != res {
index 3377270ef809ffc592c95e745bcb32594e54c4ca..0d9637e696b720d2d5fa5e6e58897e507486ca0c 100644 (file)
@@ -56,7 +56,7 @@ func (check *Checker) funcInst(x *operand, inst *syntax.IndexExpr) {
        }
 
        // instantiate function signature
-       res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
+       res := check.Instantiate(x.Pos(), sig, targs, poslist, true).(*Signature)
        assert(res.tparams == nil) // signature is not generic anymore
        if inferred {
                check.recordInferred(inst, targs, res)
@@ -326,7 +326,7 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
                }
 
                // compute result signature
-               rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature)
+               rsig = check.Instantiate(call.Pos(), sig, targs, nil, true).(*Signature)
                assert(rsig.tparams == nil) // signature is not generic anymore
                check.recordInferred(call, targs, rsig)
 
index b133fd1e65df1e3c57c43b02f4bb3257309b994e..65c201550736f392d51500d12b57b36869003a10 100644 (file)
@@ -16,6 +16,7 @@ type instance struct {
        base    *Named       // parameterized type to be instantiated
        targs   []Type       // type arguments
        poslist []syntax.Pos // position of each targ; for error reporting only
+       verify  bool         // if set, constraint satisfaction is verified
        value   Type         // base[targs...] after instantiation or Typ[Invalid]; nil if not yet set
 }
 
@@ -25,7 +26,7 @@ type instance struct {
 func (t *instance) expand() Type {
        v := t.value
        if v == nil {
-               v = t.check.instantiate(t.pos, t.base, t.targs, t.poslist)
+               v = t.check.Instantiate(t.pos, t.base, t.targs, t.poslist, t.verify)
                if v == nil {
                        v = Typ[Invalid]
                }
index 85c897a9097e3db9f081a11741effa6c4a33ce98..b289607de6748f26c40ce523a350172b60522074 100644 (file)
@@ -9,71 +9,19 @@ import (
        "fmt"
 )
 
-// Instantiate instantiates the type typ with the given type arguments.
-// typ must be a *Named or a *Signature type, it must be generic, and
-// its number of type parameters must match the number of provided type
-// arguments. The result is a new, instantiated (not generic) type of
-// the same kind (either a *Named or a *Signature). The type arguments
-// are not checked against the constraints of the type parameters.
-// Any methods attached to a *Named are simply copied; they are not
-// instantiated.
-func Instantiate(pos syntax.Pos, typ Type, targs []Type) (res Type) {
-       // TODO(gri) This code is basically identical to the prolog
-       //           in Checker.instantiate. Factor.
-       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:
-               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) {
-               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)
-       return (*Checker)(nil).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) (res Type) {
+func (check *Checker) InstantiateLazy(pos syntax.Pos, typ Type, targs []Type, verify bool) (res Type) {
        base := asNamed(typ)
        if base == nil {
                panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
        }
 
        return &instance{
-               check: check,
-               pos:   pos,
-               base:  base,
-               targs: targs,
+               check:  check,
+               pos:    pos,
+               base:   base,
+               targs:  targs,
+               verify: verify,
        }
 }
index a51d0c43d5aa9cdc92f267939acd23454d72ca22..f7f191f629223cc8608f827f0c1a4ea38c12d0d0 100644 (file)
@@ -33,7 +33,7 @@ func TestSizeof(t *testing.T) {
                {Chan{}, 12, 24},
                {Named{}, 84, 160},
                {TypeParam{}, 28, 48},
-               {instance{}, 52, 96},
+               {instance{}, 56, 104},
                {top{}, 0, 0},
 
                // Objects
index 6e4e778b208f8d2ab201f93af37dff1704fc2ddc..32cf52737259f4240200d47663c64cd86fe7f663 100644 (file)
@@ -53,8 +53,24 @@ func (m *substMap) lookup(tpar *TypeParam) Type {
        return tpar
 }
 
-func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, poslist []syntax.Pos) (res Type) {
-       if check.conf.Trace {
+// 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() {
@@ -70,7 +86,7 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, poslis
                }()
        }
 
-       assert(len(poslist) <= len(targs))
+       assert(len(posList) <= len(targs))
 
        // TODO(gri) What is better here: work with TypeParams, or work with TypeNames?
        var tparams []*TypeName
@@ -97,18 +113,19 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, poslis
                        // anymore; we need to set tparams to nil.
                        res.(*Signature).tparams = nil
                }()
-
        default:
-               check.dump("%v: cannot instantiate %v", pos, typ)
-               unreachable() // only defined types and (defined) functions can be generic
-
+               // 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
-               check.errorf(pos, "got %d arguments but %d type parameters", len(targs), len(tparams))
-               return Typ[Invalid]
+               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 {
@@ -118,15 +135,17 @@ func (check *Checker) instantiate(pos syntax.Pos, typ Type, targs []Type, poslis
        smap := makeSubstMap(tparams, targs)
 
        // check bounds
-       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
+       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
+                       }
                }
        }
 
index e861f7e78489df0866653505b6a3854b18f19ccf..d69dd3c496ffb53dc5e765b3dcc907f5cba49241 100644 (file)
@@ -423,12 +423,14 @@ func (check *Checker) instantiatedType(x syntax.Expr, targs []syntax.Expr, def *
        // create a new type instance rather than instantiate the type
        // TODO(gri) should do argument number check here rather than
        //           when instantiating the type?
+       // TODO(gri) use InstantiateLazy here (cleanup)
        typ := new(instance)
        def.setUnderlying(typ)
 
        typ.check = check
        typ.pos = x.Pos()
        typ.base = base
+       typ.verify = true
 
        // evaluate arguments (always)
        typ.targs = check.typeList(targs)