2021-02-03 15:45:26 -08:00
|
|
|
// Copyright 2021 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
// This file will evolve, since we plan to do a mix of stenciling and passing
|
|
|
|
|
// around dictionaries.
|
|
|
|
|
|
|
|
|
|
package noder
|
|
|
|
|
|
|
|
|
|
import (
|
2021-06-21 17:04:59 -07:00
|
|
|
"bytes"
|
2021-02-03 15:45:26 -08:00
|
|
|
"cmd/compile/internal/base"
|
|
|
|
|
"cmd/compile/internal/ir"
|
2021-06-07 18:13:15 -07:00
|
|
|
"cmd/compile/internal/objw"
|
2021-04-16 14:06:50 -07:00
|
|
|
"cmd/compile/internal/reflectdata"
|
2021-02-03 15:45:26 -08:00
|
|
|
"cmd/compile/internal/typecheck"
|
|
|
|
|
"cmd/compile/internal/types"
|
2021-06-07 18:13:15 -07:00
|
|
|
"cmd/internal/obj"
|
2021-06-04 17:19:09 -07:00
|
|
|
"cmd/internal/src"
|
2021-02-03 15:45:26 -08:00
|
|
|
"fmt"
|
2021-04-16 14:06:50 -07:00
|
|
|
"go/constant"
|
2021-06-21 17:04:59 -07:00
|
|
|
"strconv"
|
2021-02-03 15:45:26 -08:00
|
|
|
)
|
|
|
|
|
|
2021-07-21 19:17:20 -07:00
|
|
|
// Enable extra consistency checks.
|
|
|
|
|
const doubleCheck = true
|
|
|
|
|
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
func assert(p bool) {
|
2021-07-20 13:28:12 -07:00
|
|
|
base.Assert(p)
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
}
|
|
|
|
|
|
2021-06-07 18:13:15 -07:00
|
|
|
// Temporary - for outputting information on derived types, dictionaries, sub-dictionaries.
|
|
|
|
|
// Turn off when running tests.
|
|
|
|
|
var infoPrintMode = false
|
|
|
|
|
|
|
|
|
|
func infoPrint(format string, a ...interface{}) {
|
|
|
|
|
if infoPrintMode {
|
|
|
|
|
fmt.Printf(format, a...)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
// stencil scans functions for instantiated generic function calls and creates the
|
|
|
|
|
// required instantiations for simple generic functions. It also creates
|
|
|
|
|
// instantiated methods for all fully-instantiated generic types that have been
|
|
|
|
|
// encountered already or new ones that are encountered during the stenciling
|
|
|
|
|
// process.
|
2021-02-03 15:45:26 -08:00
|
|
|
func (g *irgen) stencil() {
|
2021-06-28 18:04:58 -07:00
|
|
|
g.instInfoMap = make(map[*types.Sym]*instInfo)
|
2021-06-07 18:13:15 -07:00
|
|
|
g.gfInfoMap = make(map[*types.Sym]*gfInfo)
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
|
|
|
|
|
// Instantiate the methods of instantiated generic types that we have seen so far.
|
|
|
|
|
g.instantiateMethods()
|
|
|
|
|
|
2021-02-08 10:23:05 -08:00
|
|
|
// Don't use range(g.target.Decls) - we also want to process any new instantiated
|
|
|
|
|
// functions that are created during this loop, in order to handle generic
|
|
|
|
|
// functions calling other generic functions.
|
|
|
|
|
for i := 0; i < len(g.target.Decls); i++ {
|
|
|
|
|
decl := g.target.Decls[i]
|
2021-02-21 10:54:38 -08:00
|
|
|
|
|
|
|
|
// Look for function instantiations in bodies of non-generic
|
|
|
|
|
// functions or in global assignments (ignore global type and
|
|
|
|
|
// constant declarations).
|
|
|
|
|
switch decl.Op() {
|
|
|
|
|
case ir.ODCLFUNC:
|
|
|
|
|
if decl.Type().HasTParam() {
|
|
|
|
|
// Skip any generic functions
|
|
|
|
|
continue
|
|
|
|
|
}
|
2021-04-26 17:52:53 -07:00
|
|
|
// transformCall() below depends on CurFunc being set.
|
|
|
|
|
ir.CurFunc = decl.(*ir.Func)
|
2021-02-21 10:54:38 -08:00
|
|
|
|
2021-04-14 14:54:14 -07:00
|
|
|
case ir.OAS, ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV, ir.OASOP:
|
|
|
|
|
// These are all the various kinds of global assignments,
|
|
|
|
|
// whose right-hand-sides might contain a function
|
|
|
|
|
// instantiation.
|
2021-02-21 10:54:38 -08:00
|
|
|
|
|
|
|
|
default:
|
2021-04-14 14:54:14 -07:00
|
|
|
// The other possible ops at the top level are ODCLCONST
|
|
|
|
|
// and ODCLTYPE, which don't have any function
|
|
|
|
|
// instantiations.
|
2021-02-03 15:45:26 -08:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-21 10:54:38 -08:00
|
|
|
// For all non-generic code, search for any function calls using
|
|
|
|
|
// generic function instantiations. Then create the needed
|
|
|
|
|
// instantiated function if it hasn't been created yet, and change
|
|
|
|
|
// to calling that function directly.
|
2021-02-03 15:45:26 -08:00
|
|
|
modified := false
|
2021-04-16 14:06:50 -07:00
|
|
|
closureRequired := false
|
2021-06-28 18:04:58 -07:00
|
|
|
// declInfo will be non-nil exactly if we are scanning an instantiated function
|
|
|
|
|
declInfo := g.instInfoMap[decl.Sym()]
|
|
|
|
|
|
2021-02-21 10:54:38 -08:00
|
|
|
ir.Visit(decl, func(n ir.Node) {
|
|
|
|
|
if n.Op() == ir.OFUNCINST {
|
2021-04-16 14:06:50 -07:00
|
|
|
// generic F, not immediately called
|
|
|
|
|
closureRequired = true
|
2021-02-21 10:54:38 -08:00
|
|
|
}
|
2021-07-18 11:09:12 -07:00
|
|
|
if (n.Op() == ir.OMETHEXPR || n.Op() == ir.OMETHVALUE) && len(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) > 0 && !types.IsInterfaceMethod(n.(*ir.SelectorExpr).Selection.Type) {
|
|
|
|
|
// T.M or x.M, where T or x is generic, but not immediately
|
2021-06-18 14:09:21 -07:00
|
|
|
// called. Not necessary if the method selected is
|
|
|
|
|
// actually for an embedded interface field.
|
2021-04-16 14:06:50 -07:00
|
|
|
closureRequired = true
|
2021-02-03 15:45:26 -08:00
|
|
|
}
|
2021-04-16 14:06:50 -07:00
|
|
|
if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST {
|
|
|
|
|
// We have found a function call using a generic function
|
|
|
|
|
// instantiation.
|
|
|
|
|
call := n.(*ir.CallExpr)
|
|
|
|
|
inst := call.X.(*ir.InstExpr)
|
2021-07-02 12:32:38 -07:00
|
|
|
nameNode, isMeth := g.getInstNameNode(inst)
|
|
|
|
|
targs := typecheck.TypesOf(inst.Targs)
|
|
|
|
|
st := g.getInstantiation(nameNode, targs, isMeth)
|
|
|
|
|
dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, nameNode, targs, isMeth)
|
|
|
|
|
if infoPrintMode {
|
|
|
|
|
dictkind := "Main dictionary"
|
|
|
|
|
if usingSubdict {
|
2021-06-28 18:04:58 -07:00
|
|
|
dictkind = "Sub-dictionary"
|
|
|
|
|
}
|
2021-06-27 01:28:38 +07:00
|
|
|
if inst.X.Op() == ir.OMETHVALUE {
|
2021-06-28 18:04:58 -07:00
|
|
|
fmt.Printf("%s in %v at generic method call: %v - %v\n", dictkind, decl, inst.X, call)
|
2021-06-07 18:13:15 -07:00
|
|
|
} else {
|
2021-06-28 18:04:58 -07:00
|
|
|
fmt.Printf("%s in %v at generic function call: %v - %v\n", dictkind, decl, inst.X, call)
|
2021-06-07 18:13:15 -07:00
|
|
|
}
|
|
|
|
|
}
|
2021-04-16 14:06:50 -07:00
|
|
|
// Replace the OFUNCINST with a direct reference to the
|
|
|
|
|
// new stenciled function
|
|
|
|
|
call.X = st.Nname
|
2021-06-27 01:28:38 +07:00
|
|
|
if inst.X.Op() == ir.OMETHVALUE {
|
2021-04-16 14:06:50 -07:00
|
|
|
// When we create an instantiation of a method
|
|
|
|
|
// call, we make it a function. So, move the
|
|
|
|
|
// receiver to be the first arg of the function
|
|
|
|
|
// call.
|
|
|
|
|
call.Args.Prepend(inst.X.(*ir.SelectorExpr).X)
|
|
|
|
|
}
|
2021-06-09 19:30:16 -07:00
|
|
|
|
2021-04-16 14:06:50 -07:00
|
|
|
// Add dictionary to argument list.
|
2021-07-02 12:32:38 -07:00
|
|
|
call.Args.Prepend(dictValue)
|
2021-04-16 14:06:50 -07:00
|
|
|
// Transform the Call now, which changes OCALL
|
|
|
|
|
// to OCALLFUNC and does typecheckaste/assignconvfn.
|
|
|
|
|
transformCall(call)
|
|
|
|
|
modified = true
|
|
|
|
|
}
|
|
|
|
|
if n.Op() == ir.OCALLMETH && n.(*ir.CallExpr).X.Op() == ir.ODOTMETH && len(deref(n.(*ir.CallExpr).X.Type().Recv().Type).RParams()) > 0 {
|
|
|
|
|
// Method call on a generic type, which was instantiated by stenciling.
|
|
|
|
|
// Method calls on explicitly instantiated types will have an OFUNCINST
|
|
|
|
|
// and are handled above.
|
|
|
|
|
call := n.(*ir.CallExpr)
|
|
|
|
|
meth := call.X.(*ir.SelectorExpr)
|
|
|
|
|
targs := deref(meth.Type().Recv().Type).RParams()
|
|
|
|
|
|
|
|
|
|
t := meth.X.Type()
|
|
|
|
|
baseSym := deref(t).OrigSym
|
|
|
|
|
baseType := baseSym.Def.(*ir.Name).Type()
|
|
|
|
|
var gf *ir.Name
|
|
|
|
|
for _, m := range baseType.Methods().Slice() {
|
|
|
|
|
if meth.Sel == m.Sym {
|
|
|
|
|
gf = m.Nname.(*ir.Name)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-02 12:32:38 -07:00
|
|
|
st := g.getInstantiation(gf, targs, true)
|
|
|
|
|
dictValue, usingSubdict := g.getDictOrSubdict(declInfo, n, gf, targs, true)
|
[dev.typeparams] Add optional sub-dict entry for typeparam bound calls
In the case that a generic function/method f does a method call on a
type param allowed by its bound, an instantiation of f may do a direct
method call of a concrete type or a method call defined on a generic
type, depending on whether the passed type in a concrete type or an
instantiated type with the appropriate method defined. See the test case
boundmethod.go added to this change.
In order to keep the dictionary format the same for all instantiations
of a generic function/method, I decided to have an optional
sub-dictionary entry for "bounds" calls. At the point that we are
creating the actual dictionary, we can then fill in the needed
sub-dictionary, if the type arg is an instantiated type, or a zeroed
dictionary entry, if type arg is not instantiated and the method will be
on a concrete type.
In order to implement this, I now fill in n.Selection for "bounds"
method calls in generic functions as well. Also, I need to calculate
n.Selection correctly during import for the case where it is now set -
method calls on generic types, and bounds calls on typeparams.
With this change, the dictionaries/sub-dictionaries are correct for
absdiff.go. The new test boundmethod.go illustrates the case where the
bound sub-dict entry is not used for a dictionary for stringify[myint],
but is used for a dictionary for stringify[StringInt[myint]].
Change-Id: Ie2bcb971b7019a9f1da68c97eb03da2333327457
Reviewed-on: https://go-review.googlesource.com/c/go/+/333456
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
2021-07-02 17:51:20 -07:00
|
|
|
// We have to be using a subdictionary, since this is
|
|
|
|
|
// a generic method call.
|
|
|
|
|
assert(usingSubdict)
|
2021-07-02 12:32:38 -07:00
|
|
|
|
2021-04-16 14:06:50 -07:00
|
|
|
call.SetOp(ir.OCALL)
|
|
|
|
|
call.X = st.Nname
|
2021-07-02 12:32:38 -07:00
|
|
|
call.Args.Prepend(dictValue, meth.X)
|
2021-04-16 14:06:50 -07:00
|
|
|
// Transform the Call now, which changes OCALL
|
|
|
|
|
// to OCALLFUNC and does typecheckaste/assignconvfn.
|
|
|
|
|
transformCall(call)
|
|
|
|
|
modified = true
|
2021-02-11 10:50:20 -08:00
|
|
|
}
|
2021-02-03 15:45:26 -08:00
|
|
|
})
|
2021-02-21 10:54:38 -08:00
|
|
|
|
2021-04-16 14:06:50 -07:00
|
|
|
// If we found a reference to a generic instantiation that wasn't an
|
|
|
|
|
// immediate call, then traverse the nodes of decl again (with
|
2021-02-21 10:54:38 -08:00
|
|
|
// EditChildren rather than Visit), where we actually change the
|
2021-04-16 14:06:50 -07:00
|
|
|
// reference to the instantiation to a closure that captures the
|
|
|
|
|
// dictionary, then does a direct call.
|
2021-02-21 10:54:38 -08:00
|
|
|
// EditChildren is more expensive than Visit, so we only do this
|
2021-05-07 13:20:34 -07:00
|
|
|
// in the infrequent case of an OFUNCINST without a corresponding
|
2021-02-21 10:54:38 -08:00
|
|
|
// call.
|
2021-04-16 14:06:50 -07:00
|
|
|
if closureRequired {
|
2021-07-18 11:09:12 -07:00
|
|
|
modified = true
|
2021-02-21 10:54:38 -08:00
|
|
|
var edit func(ir.Node) ir.Node
|
2021-06-03 15:39:23 -07:00
|
|
|
var outer *ir.Func
|
|
|
|
|
if f, ok := decl.(*ir.Func); ok {
|
|
|
|
|
outer = f
|
|
|
|
|
}
|
2021-02-21 10:54:38 -08:00
|
|
|
edit = func(x ir.Node) ir.Node {
|
2021-07-18 11:09:12 -07:00
|
|
|
if x.Op() == ir.OFUNCINST {
|
|
|
|
|
child := x.(*ir.InstExpr).X
|
|
|
|
|
if child.Op() == ir.OMETHEXPR || child.Op() == ir.OMETHVALUE {
|
|
|
|
|
// Call EditChildren on child (x.X),
|
|
|
|
|
// not x, so that we don't do
|
|
|
|
|
// buildClosure() on the
|
|
|
|
|
// METHEXPR/METHVALUE nodes as well.
|
|
|
|
|
ir.EditChildren(child, edit)
|
|
|
|
|
return g.buildClosure(outer, x)
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-02-21 10:54:38 -08:00
|
|
|
ir.EditChildren(x, edit)
|
2021-04-16 14:06:50 -07:00
|
|
|
switch {
|
|
|
|
|
case x.Op() == ir.OFUNCINST:
|
2021-06-03 15:39:23 -07:00
|
|
|
return g.buildClosure(outer, x)
|
2021-07-18 11:09:12 -07:00
|
|
|
case (x.Op() == ir.OMETHEXPR || x.Op() == ir.OMETHVALUE) &&
|
|
|
|
|
len(deref(x.(*ir.SelectorExpr).X.Type()).RParams()) > 0 &&
|
|
|
|
|
!types.IsInterfaceMethod(x.(*ir.SelectorExpr).Selection.Type):
|
2021-06-03 15:39:23 -07:00
|
|
|
return g.buildClosure(outer, x)
|
2021-04-16 14:06:50 -07:00
|
|
|
}
|
2021-02-21 10:54:38 -08:00
|
|
|
return x
|
|
|
|
|
}
|
|
|
|
|
edit(decl)
|
|
|
|
|
}
|
2021-02-03 15:45:26 -08:00
|
|
|
if base.Flag.W > 1 && modified {
|
|
|
|
|
ir.Dump(fmt.Sprintf("\nmodified %v", decl), decl)
|
|
|
|
|
}
|
2021-04-26 17:52:53 -07:00
|
|
|
ir.CurFunc = nil
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
// We may have seen new fully-instantiated generic types while
|
|
|
|
|
// instantiating any needed functions/methods in the above
|
|
|
|
|
// function. If so, instantiate all the methods of those types
|
|
|
|
|
// (which will then lead to more function/methods to scan in the loop).
|
|
|
|
|
g.instantiateMethods()
|
2021-02-03 15:45:26 -08:00
|
|
|
}
|
|
|
|
|
|
[dev.typeparams] cmd/compile: add dictionary entries for itab conversion
This fix the case where a type param or derived type is converted to a
non-empty interface. Previously, we were converting to an empty
interface and then using DOTTYPE to convert to the correct non-empty
interface. In that case, we can get the needed itab directly from the
dictionary. This is needed for correctness from shapes, if the
destination interface is parameterized, else we will incorrectly convert
to the shape version of the interface.
Creating/writing an itab can involve generating wrappers for a bunch of
methods, which may use dictionaries. So, all the
dictionaries/instantiations are being generated on the fly and have
recursive relationships, it is simplest to finish creating/writing the
itabs at the end of the stenciling phase. So, we create a list of the
dictionaries which need to be completed by writing out their itab
entries.
The existing tests ordered.go, ifaceconv.go, and issue44688.go make use
of this optimization.
Got itab conversions for bound calls working, except for 13.go.
Also, want to get rid of the concretify, but I think we need more info
on the Bound from types2.
Change-Id: If552958a7b8a435500d6cc42c401572c367b30d1
Reviewed-on: https://go-review.googlesource.com/c/go/+/336993
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2021-07-12 19:34:15 -07:00
|
|
|
g.finalizeSyms()
|
2021-02-03 15:45:26 -08:00
|
|
|
}
|
|
|
|
|
|
2021-04-16 14:06:50 -07:00
|
|
|
// buildClosure makes a closure to implement x, a OFUNCINST or OMETHEXPR
|
2021-06-03 15:39:23 -07:00
|
|
|
// of generic type. outer is the containing function (or nil if closure is
|
|
|
|
|
// in a global assignment instead of a function).
|
2021-04-16 14:06:50 -07:00
|
|
|
func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
|
|
|
|
|
pos := x.Pos()
|
|
|
|
|
var target *ir.Func // target instantiated function/method
|
|
|
|
|
var dictValue ir.Node // dictionary to use
|
|
|
|
|
var rcvrValue ir.Node // receiver, if a method value
|
|
|
|
|
typ := x.Type() // type of the closure
|
2021-06-28 18:04:58 -07:00
|
|
|
var outerInfo *instInfo
|
|
|
|
|
if outer != nil {
|
|
|
|
|
outerInfo = g.instInfoMap[outer.Sym()]
|
|
|
|
|
}
|
|
|
|
|
usingSubdict := false
|
2021-07-06 10:53:00 -07:00
|
|
|
valueMethod := false
|
2021-04-16 14:06:50 -07:00
|
|
|
if x.Op() == ir.OFUNCINST {
|
|
|
|
|
inst := x.(*ir.InstExpr)
|
|
|
|
|
|
|
|
|
|
// Type arguments we're instantiating with.
|
|
|
|
|
targs := typecheck.TypesOf(inst.Targs)
|
|
|
|
|
|
|
|
|
|
// Find the generic function/method.
|
|
|
|
|
var gf *ir.Name
|
|
|
|
|
if inst.X.Op() == ir.ONAME {
|
|
|
|
|
// Instantiating a generic function call.
|
|
|
|
|
gf = inst.X.(*ir.Name)
|
2021-06-27 01:28:38 +07:00
|
|
|
} else if inst.X.Op() == ir.OMETHVALUE {
|
2021-04-16 14:06:50 -07:00
|
|
|
// Instantiating a method value x.M.
|
|
|
|
|
se := inst.X.(*ir.SelectorExpr)
|
|
|
|
|
rcvrValue = se.X
|
|
|
|
|
gf = se.Selection.Nname.(*ir.Name)
|
|
|
|
|
} else {
|
|
|
|
|
panic("unhandled")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// target is the instantiated function we're trying to call.
|
|
|
|
|
// For functions, the target expects a dictionary as its first argument.
|
|
|
|
|
// For method values, the target expects a dictionary and the receiver
|
|
|
|
|
// as its first two arguments.
|
2021-06-07 18:13:15 -07:00
|
|
|
// dictValue is the value to use for the dictionary argument.
|
2021-07-02 12:32:38 -07:00
|
|
|
target = g.getInstantiation(gf, targs, rcvrValue != nil)
|
|
|
|
|
dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, rcvrValue != nil)
|
|
|
|
|
if infoPrintMode {
|
|
|
|
|
dictkind := "Main dictionary"
|
|
|
|
|
if usingSubdict {
|
2021-06-28 18:04:58 -07:00
|
|
|
dictkind = "Sub-dictionary"
|
|
|
|
|
}
|
2021-06-07 18:13:15 -07:00
|
|
|
if rcvrValue == nil {
|
2021-06-28 18:04:58 -07:00
|
|
|
fmt.Printf("%s in %v for generic function value %v\n", dictkind, outer, inst.X)
|
2021-06-07 18:13:15 -07:00
|
|
|
} else {
|
2021-06-28 18:04:58 -07:00
|
|
|
fmt.Printf("%s in %v for generic method value %v\n", dictkind, outer, inst.X)
|
2021-06-07 18:13:15 -07:00
|
|
|
}
|
2021-04-16 14:06:50 -07:00
|
|
|
}
|
2021-07-18 11:09:12 -07:00
|
|
|
} else { // ir.OMETHEXPR or ir.METHVALUE
|
2021-04-16 14:06:50 -07:00
|
|
|
// Method expression T.M where T is a generic type.
|
|
|
|
|
se := x.(*ir.SelectorExpr)
|
2021-07-06 10:53:00 -07:00
|
|
|
targs := deref(se.X.Type()).RParams()
|
2021-04-16 14:06:50 -07:00
|
|
|
if len(targs) == 0 {
|
2021-07-06 10:53:00 -07:00
|
|
|
panic("bad")
|
2021-04-16 14:06:50 -07:00
|
|
|
}
|
2021-07-18 11:09:12 -07:00
|
|
|
if x.Op() == ir.OMETHVALUE {
|
|
|
|
|
rcvrValue = se.X
|
|
|
|
|
}
|
2021-06-18 14:09:21 -07:00
|
|
|
|
|
|
|
|
// se.X.Type() is the top-level type of the method expression. To
|
|
|
|
|
// correctly handle method expressions involving embedded fields,
|
|
|
|
|
// look up the generic method below using the type of the receiver
|
|
|
|
|
// of se.Selection, since that will be the type that actually has
|
|
|
|
|
// the method.
|
|
|
|
|
recv := deref(se.Selection.Type.Recv().Type)
|
|
|
|
|
baseType := recv.OrigSym.Def.Type()
|
2021-04-16 14:06:50 -07:00
|
|
|
var gf *ir.Name
|
|
|
|
|
for _, m := range baseType.Methods().Slice() {
|
|
|
|
|
if se.Sel == m.Sym {
|
|
|
|
|
gf = m.Nname.(*ir.Name)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-06 10:53:00 -07:00
|
|
|
if !gf.Type().Recv().Type.IsPtr() {
|
|
|
|
|
// Remember if value method, so we can detect (*T).M case.
|
|
|
|
|
valueMethod = true
|
|
|
|
|
}
|
2021-07-02 12:32:38 -07:00
|
|
|
target = g.getInstantiation(gf, targs, true)
|
|
|
|
|
dictValue, usingSubdict = g.getDictOrSubdict(outerInfo, x, gf, targs, true)
|
|
|
|
|
if infoPrintMode {
|
|
|
|
|
dictkind := "Main dictionary"
|
|
|
|
|
if usingSubdict {
|
2021-06-28 18:04:58 -07:00
|
|
|
dictkind = "Sub-dictionary"
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf("%s in %v for method expression %v\n", dictkind, outer, x)
|
2021-06-07 18:13:15 -07:00
|
|
|
}
|
2021-04-16 14:06:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build a closure to implement a function instantiation.
|
|
|
|
|
//
|
|
|
|
|
// func f[T any] (int, int) (int, int) { ...whatever... }
|
|
|
|
|
//
|
|
|
|
|
// Then any reference to f[int] not directly called gets rewritten to
|
|
|
|
|
//
|
|
|
|
|
// .dictN := ... dictionary to use ...
|
|
|
|
|
// func(a0, a1 int) (r0, r1 int) {
|
|
|
|
|
// return .inst.f[int](.dictN, a0, a1)
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// Similarly for method expressions,
|
|
|
|
|
//
|
|
|
|
|
// type g[T any] ....
|
|
|
|
|
// func (rcvr g[T]) f(a0, a1 int) (r0, r1 int) { ... }
|
|
|
|
|
//
|
|
|
|
|
// Any reference to g[int].f not directly called gets rewritten to
|
|
|
|
|
//
|
|
|
|
|
// .dictN := ... dictionary to use ...
|
|
|
|
|
// func(rcvr g[int], a0, a1 int) (r0, r1 int) {
|
|
|
|
|
// return .inst.g[int].f(.dictN, rcvr, a0, a1)
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// Also method values
|
|
|
|
|
//
|
|
|
|
|
// var x g[int]
|
|
|
|
|
//
|
|
|
|
|
// Any reference to x.f not directly called gets rewritten to
|
|
|
|
|
//
|
|
|
|
|
// .dictN := ... dictionary to use ...
|
|
|
|
|
// x2 := x
|
|
|
|
|
// func(a0, a1 int) (r0, r1 int) {
|
|
|
|
|
// return .inst.g[int].f(.dictN, x2, a0, a1)
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// Make a new internal function.
|
2021-06-12 07:33:18 -07:00
|
|
|
fn := ir.NewClosureFunc(pos, outer != nil)
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
ir.NameClosure(fn.OClosure, outer)
|
2021-04-16 14:06:50 -07:00
|
|
|
|
|
|
|
|
// This is the dictionary we want to use.
|
2021-06-03 15:39:23 -07:00
|
|
|
// It may be a constant, or it may be a dictionary acquired from the outer function's dictionary.
|
|
|
|
|
// For the latter, dictVar is a variable in the outer function's scope, set to the subdictionary
|
|
|
|
|
// read from the outer function's dictionary.
|
|
|
|
|
var dictVar *ir.Name
|
|
|
|
|
var dictAssign *ir.AssignStmt
|
|
|
|
|
if outer != nil {
|
|
|
|
|
// Note: for now this is a compile-time constant, so we don't really need a closure
|
|
|
|
|
// to capture it (a wrapper function would work just as well). But eventually it
|
|
|
|
|
// will be a read of a subdictionary from the parent dictionary.
|
|
|
|
|
dictVar = ir.NewNameAt(pos, typecheck.LookupNum(".dict", g.dnum))
|
|
|
|
|
g.dnum++
|
|
|
|
|
dictVar.Class = ir.PAUTO
|
|
|
|
|
typed(types.Types[types.TUINTPTR], dictVar)
|
|
|
|
|
dictVar.Curfn = outer
|
|
|
|
|
dictAssign = ir.NewAssignStmt(pos, dictVar, dictValue)
|
|
|
|
|
dictAssign.SetTypecheck(1)
|
|
|
|
|
dictVar.Defn = dictAssign
|
|
|
|
|
outer.Dcl = append(outer.Dcl, dictVar)
|
|
|
|
|
}
|
2021-04-16 14:06:50 -07:00
|
|
|
// assign the receiver to a temporary.
|
|
|
|
|
var rcvrVar *ir.Name
|
|
|
|
|
var rcvrAssign ir.Node
|
|
|
|
|
if rcvrValue != nil {
|
|
|
|
|
rcvrVar = ir.NewNameAt(pos, typecheck.LookupNum(".rcvr", g.dnum))
|
|
|
|
|
g.dnum++
|
|
|
|
|
rcvrVar.Class = ir.PAUTO
|
|
|
|
|
typed(rcvrValue.Type(), rcvrVar)
|
|
|
|
|
rcvrVar.Curfn = outer
|
|
|
|
|
rcvrAssign = ir.NewAssignStmt(pos, rcvrVar, rcvrValue)
|
|
|
|
|
rcvrAssign.SetTypecheck(1)
|
|
|
|
|
rcvrVar.Defn = rcvrAssign
|
|
|
|
|
outer.Dcl = append(outer.Dcl, rcvrVar)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build formal argument and return lists.
|
|
|
|
|
var formalParams []*types.Field // arguments of closure
|
|
|
|
|
var formalResults []*types.Field // returns of closure
|
|
|
|
|
for i := 0; i < typ.NumParams(); i++ {
|
|
|
|
|
t := typ.Params().Field(i).Type
|
|
|
|
|
arg := ir.NewNameAt(pos, typecheck.LookupNum("a", i))
|
|
|
|
|
arg.Class = ir.PPARAM
|
|
|
|
|
typed(t, arg)
|
|
|
|
|
arg.Curfn = fn
|
|
|
|
|
fn.Dcl = append(fn.Dcl, arg)
|
|
|
|
|
f := types.NewField(pos, arg.Sym(), t)
|
|
|
|
|
f.Nname = arg
|
|
|
|
|
formalParams = append(formalParams, f)
|
|
|
|
|
}
|
|
|
|
|
for i := 0; i < typ.NumResults(); i++ {
|
|
|
|
|
t := typ.Results().Field(i).Type
|
|
|
|
|
result := ir.NewNameAt(pos, typecheck.LookupNum("r", i)) // TODO: names not needed?
|
|
|
|
|
result.Class = ir.PPARAMOUT
|
|
|
|
|
typed(t, result)
|
|
|
|
|
result.Curfn = fn
|
|
|
|
|
fn.Dcl = append(fn.Dcl, result)
|
|
|
|
|
f := types.NewField(pos, result.Sym(), t)
|
|
|
|
|
f.Nname = result
|
|
|
|
|
formalResults = append(formalResults, f)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build an internal function with the right signature.
|
|
|
|
|
closureType := types.NewSignature(x.Type().Pkg(), nil, nil, formalParams, formalResults)
|
|
|
|
|
typed(closureType, fn.Nname)
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
typed(x.Type(), fn.OClosure)
|
2021-04-16 14:06:50 -07:00
|
|
|
fn.SetTypecheck(1)
|
|
|
|
|
|
|
|
|
|
// Build body of closure. This involves just calling the wrapped function directly
|
|
|
|
|
// with the additional dictionary argument.
|
|
|
|
|
|
2021-06-03 15:39:23 -07:00
|
|
|
// First, figure out the dictionary argument.
|
|
|
|
|
var dict2Var ir.Node
|
2021-06-28 18:04:58 -07:00
|
|
|
if usingSubdict {
|
|
|
|
|
// Capture sub-dictionary calculated in the outer function
|
2021-06-03 15:39:23 -07:00
|
|
|
dict2Var = ir.CaptureName(pos, fn, dictVar)
|
2021-06-28 18:04:58 -07:00
|
|
|
typed(types.Types[types.TUINTPTR], dict2Var)
|
2021-06-03 15:39:23 -07:00
|
|
|
} else {
|
2021-06-28 18:04:58 -07:00
|
|
|
// Static dictionary, so can be used directly in the closure
|
2021-06-03 15:39:23 -07:00
|
|
|
dict2Var = dictValue
|
|
|
|
|
}
|
2021-04-16 14:06:50 -07:00
|
|
|
// Also capture the receiver variable.
|
|
|
|
|
var rcvr2Var *ir.Name
|
|
|
|
|
if rcvrValue != nil {
|
|
|
|
|
rcvr2Var = ir.CaptureName(pos, fn, rcvrVar)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build arguments to call inside the closure.
|
|
|
|
|
var args []ir.Node
|
|
|
|
|
|
|
|
|
|
// First the dictionary argument.
|
|
|
|
|
args = append(args, dict2Var)
|
|
|
|
|
// Then the receiver.
|
|
|
|
|
if rcvrValue != nil {
|
|
|
|
|
args = append(args, rcvr2Var)
|
|
|
|
|
}
|
|
|
|
|
// Then all the other arguments (including receiver for method expressions).
|
|
|
|
|
for i := 0; i < typ.NumParams(); i++ {
|
2021-06-18 14:09:21 -07:00
|
|
|
if x.Op() == ir.OMETHEXPR && i == 0 {
|
|
|
|
|
// If we are doing a method expression, we need to
|
|
|
|
|
// explicitly traverse any embedded fields in the receiver
|
|
|
|
|
// argument in order to call the method instantiation.
|
2021-07-06 10:53:00 -07:00
|
|
|
arg0 := formalParams[0].Nname.(ir.Node)
|
|
|
|
|
arg0 = typecheck.AddImplicitDots(ir.NewSelectorExpr(base.Pos, ir.OXDOT, arg0, x.(*ir.SelectorExpr).Sel)).X
|
|
|
|
|
if valueMethod && arg0.Type().IsPtr() {
|
|
|
|
|
// For handling the (*T).M case: if we have a pointer
|
|
|
|
|
// receiver after following all the embedded fields,
|
|
|
|
|
// but it's a value method, add a star operator.
|
|
|
|
|
arg0 = ir.NewStarExpr(arg0.Pos(), arg0)
|
|
|
|
|
}
|
|
|
|
|
args = append(args, arg0)
|
2021-06-18 14:09:21 -07:00
|
|
|
} else {
|
|
|
|
|
args = append(args, formalParams[i].Nname.(*ir.Name))
|
|
|
|
|
}
|
2021-04-16 14:06:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build call itself.
|
|
|
|
|
var innerCall ir.Node = ir.NewCallExpr(pos, ir.OCALL, target.Nname, args)
|
|
|
|
|
if len(formalResults) > 0 {
|
|
|
|
|
innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall})
|
|
|
|
|
}
|
|
|
|
|
// Finish building body of closure.
|
|
|
|
|
ir.CurFunc = fn
|
|
|
|
|
// TODO: set types directly here instead of using typecheck.Stmt
|
|
|
|
|
typecheck.Stmt(innerCall)
|
|
|
|
|
ir.CurFunc = nil
|
|
|
|
|
fn.Body = []ir.Node{innerCall}
|
|
|
|
|
|
|
|
|
|
// We're all done with the captured dictionary (and receiver, for method values).
|
|
|
|
|
ir.FinishCaptureNames(pos, outer, fn)
|
|
|
|
|
|
|
|
|
|
// Make a closure referencing our new internal function.
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
c := ir.UseClosure(fn.OClosure, g.target)
|
2021-06-03 15:39:23 -07:00
|
|
|
var init []ir.Node
|
|
|
|
|
if outer != nil {
|
|
|
|
|
init = append(init, dictAssign)
|
|
|
|
|
}
|
2021-04-16 14:06:50 -07:00
|
|
|
if rcvrValue != nil {
|
|
|
|
|
init = append(init, rcvrAssign)
|
|
|
|
|
}
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
return ir.InitExpr(init, c)
|
2021-04-16 14:06:50 -07:00
|
|
|
}
|
|
|
|
|
|
2021-07-02 12:32:38 -07:00
|
|
|
// instantiateMethods instantiates all the methods (and associated dictionaries) of
|
|
|
|
|
// all fully-instantiated generic types that have been added to g.instTypeList.
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
func (g *irgen) instantiateMethods() {
|
|
|
|
|
for i := 0; i < len(g.instTypeList); i++ {
|
|
|
|
|
typ := g.instTypeList[i]
|
2021-06-09 19:30:16 -07:00
|
|
|
if typ.HasShape() {
|
|
|
|
|
// Shape types should not have any methods.
|
|
|
|
|
continue
|
|
|
|
|
}
|
[dev.typeparams] cmd/compile: get export/import of generic types & functions working
The general idea is that we now export/import typeparams, typeparam
lists for generic types and functions, and instantiated types
(instantiations of generic types with either new typeparams or concrete
types).
This changes the export format -- the next CL in the stack adds the
export versions and checks for it in the appropriate places.
We always export/import generic function bodies, using the same code
that we use for exporting/importing the bodies of inlineable functions.
To avoid complicated scoping, we consider all type params as unique and
give them unique names for types1. We therefore include the types2 ids
(subscripts) in the export format and re-create on import. We always
access the same unique types1 typeParam type for the same typeparam
name.
We create fully-instantiated generic types and functions in the original
source package. We do an extra NeedRuntimeType() call to make sure that
the correct DWARF information is written out. We call SetDupOK(true) for
the functions/methods to have the linker automatically drop duplicate
instantiations.
Other miscellaneous details:
- Export/import of typeparam bounds works for methods (but not
typelists) for now, but will change with the typeset changes.
- Added a new types.Instantiate function roughly analogous to the
types2.Instantiate function recently added.
- Always access methods info from the original/base generic type, since
the methods of an instantiated type are not filled in (in types2 or
types1).
- New field OrigSym in types.Type to keep track of base generic type
that instantiated type was based on. We use the generic type's symbol
(OrigSym) as the link, rather than a Type pointer, since we haven't
always created the base type yet when we want to set the link (during
types2 to types1 conversion).
- Added types2.AsTypeParam(), (*types2.TypeParam).SetId()
- New test minimp.dir, which tests use of generic function Min across
packages. Another test stringimp.dir, which also exports a generic
function Stringify across packages, where the type param has a bound
(Stringer) as well. New test pairimp.dir, which tests use of generic
type Pair (with no methods) across packages.
- New test valimp.dir, which tests use of generic type (with methods
and related functions) across packages.
- Modified several other tests (adder.go, settable.go, smallest.go,
stringable.go, struct.go, sum.go) to export their generic
functions/types to show that generic functions/types can be exported
successfully (but this doesn't test import).
Change-Id: Ie61ce9d54a46d368ddc7a76c41399378963bb57f
Reviewed-on: https://go-review.googlesource.com/c/go/+/319930
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-04-13 15:37:36 -07:00
|
|
|
// Mark runtime type as needed, since this ensures that the
|
|
|
|
|
// compiler puts out the needed DWARF symbols, when this
|
|
|
|
|
// instantiated type has a different package from the local
|
|
|
|
|
// package.
|
|
|
|
|
typecheck.NeedRuntimeType(typ)
|
|
|
|
|
// Lookup the method on the base generic type, since methods may
|
|
|
|
|
// not be set on imported instantiated types.
|
|
|
|
|
baseSym := typ.OrigSym
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
baseType := baseSym.Def.(*ir.Name).Type()
|
2021-04-16 14:06:50 -07:00
|
|
|
for j, _ := range typ.Methods().Slice() {
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name)
|
2021-06-07 18:13:15 -07:00
|
|
|
// Eagerly generate the instantiations and dictionaries that implement these methods.
|
2021-04-16 14:06:50 -07:00
|
|
|
// We don't use the instantiations here, just generate them (and any
|
|
|
|
|
// further instantiations those generate, etc.).
|
|
|
|
|
// Note that we don't set the Func for any methods on instantiated
|
|
|
|
|
// types. Their signatures don't match so that would be confusing.
|
|
|
|
|
// Direct method calls go directly to the instantiations, implemented above.
|
|
|
|
|
// Indirect method calls use wrappers generated in reflectcall. Those wrappers
|
|
|
|
|
// will use these instantiations if they are needed (for interface tables or reflection).
|
2021-07-02 12:32:38 -07:00
|
|
|
_ = g.getInstantiation(baseNname, typ.RParams(), true)
|
|
|
|
|
_ = g.getDictionarySym(baseNname, typ.RParams(), true)
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
g.instTypeList = nil
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-02 12:32:38 -07:00
|
|
|
// getInstNameNode returns the name node for the method or function being instantiated, and a bool which is true if a method is being instantiated.
|
|
|
|
|
func (g *irgen) getInstNameNode(inst *ir.InstExpr) (*ir.Name, bool) {
|
2021-02-11 10:50:20 -08:00
|
|
|
if meth, ok := inst.X.(*ir.SelectorExpr); ok {
|
2021-07-02 12:32:38 -07:00
|
|
|
return meth.Selection.Nname.(*ir.Name), true
|
2021-02-11 10:50:20 -08:00
|
|
|
} else {
|
2021-07-02 12:32:38 -07:00
|
|
|
return inst.X.(*ir.Name), false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getDictOrSubdict returns, for a method/function call or reference (node n) in an
|
|
|
|
|
// instantiation (described by instInfo), a node which is accessing a sub-dictionary
|
|
|
|
|
// or main/static dictionary, as needed, and also returns a boolean indicating if a
|
|
|
|
|
// sub-dictionary was accessed. nameNode is the particular function or method being
|
|
|
|
|
// called/referenced, and targs are the type arguments.
|
|
|
|
|
func (g *irgen) getDictOrSubdict(declInfo *instInfo, n ir.Node, nameNode *ir.Name, targs []*types.Type, isMeth bool) (ir.Node, bool) {
|
|
|
|
|
var dict ir.Node
|
|
|
|
|
usingSubdict := false
|
|
|
|
|
if declInfo != nil {
|
|
|
|
|
// Get the dictionary arg via sub-dictionary reference
|
|
|
|
|
entry, ok := declInfo.dictEntryMap[n]
|
|
|
|
|
// If the entry is not found, it may be that this node did not have
|
|
|
|
|
// any type args that depend on type params, so we need a main
|
|
|
|
|
// dictionary, not a sub-dictionary.
|
|
|
|
|
if ok {
|
|
|
|
|
dict = getDictionaryEntry(n.Pos(), declInfo.dictParam, entry, declInfo.dictLen)
|
|
|
|
|
usingSubdict = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !usingSubdict {
|
|
|
|
|
dict = g.getDictionaryValue(nameNode, targs, isMeth)
|
2021-02-11 10:50:20 -08:00
|
|
|
}
|
2021-07-02 12:32:38 -07:00
|
|
|
return dict, usingSubdict
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
}
|
|
|
|
|
|
2021-06-21 17:04:59 -07:00
|
|
|
func addGcType(fl []*types.Field, t *types.Type) []*types.Field {
|
|
|
|
|
return append(fl, types.NewField(base.Pos, typecheck.Lookup("F"+strconv.Itoa(len(fl))), t))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const INTTYPE = types.TINT64 // XX fix for 32-bit arch
|
|
|
|
|
const UINTTYPE = types.TUINT64 // XX fix for 32-bit arch
|
|
|
|
|
const INTSTRING = "i8" // XX fix for 32-bit arch
|
|
|
|
|
const UINTSTRING = "u8" // XX fix for 32-bit arch
|
|
|
|
|
|
|
|
|
|
// accumGcshape adds fields to fl resulting from the GCshape transformation of
|
|
|
|
|
// type t. The string associated with the GCshape transformation of t is added to
|
|
|
|
|
// buf. fieldSym is the sym of the field associated with type t, if it is in a
|
|
|
|
|
// struct. fieldSym could be used to have special naming for blank fields, etc.
|
|
|
|
|
func accumGcshape(fl []*types.Field, buf *bytes.Buffer, t *types.Type, fieldSym *types.Sym) []*types.Field {
|
|
|
|
|
// t.Kind() is already the kind of the underlying type, so no need to
|
|
|
|
|
// reference t.Underlying() to reference the underlying type.
|
|
|
|
|
assert(t.Kind() == t.Underlying().Kind())
|
|
|
|
|
|
|
|
|
|
switch t.Kind() {
|
|
|
|
|
case types.TINT8:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TINT8])
|
|
|
|
|
buf.WriteString("i1")
|
|
|
|
|
|
|
|
|
|
case types.TUINT8:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TUINT8])
|
|
|
|
|
buf.WriteString("u1")
|
|
|
|
|
|
|
|
|
|
case types.TINT16:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TINT16])
|
|
|
|
|
buf.WriteString("i2")
|
|
|
|
|
|
|
|
|
|
case types.TUINT16:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TUINT16])
|
|
|
|
|
buf.WriteString("u2")
|
|
|
|
|
|
|
|
|
|
case types.TINT32:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TINT32])
|
|
|
|
|
buf.WriteString("i4")
|
|
|
|
|
|
|
|
|
|
case types.TUINT32:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TUINT32])
|
|
|
|
|
buf.WriteString("u4")
|
|
|
|
|
|
|
|
|
|
case types.TINT64:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TINT64])
|
|
|
|
|
buf.WriteString("i8")
|
|
|
|
|
|
|
|
|
|
case types.TUINT64:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TUINT64])
|
|
|
|
|
buf.WriteString("u8")
|
|
|
|
|
|
|
|
|
|
case types.TINT:
|
|
|
|
|
fl = addGcType(fl, types.Types[INTTYPE])
|
|
|
|
|
buf.WriteString(INTSTRING)
|
|
|
|
|
|
|
|
|
|
case types.TUINT, types.TUINTPTR:
|
|
|
|
|
fl = addGcType(fl, types.Types[UINTTYPE])
|
|
|
|
|
buf.WriteString(UINTSTRING)
|
|
|
|
|
|
|
|
|
|
case types.TCOMPLEX64:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TFLOAT32])
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TFLOAT32])
|
|
|
|
|
buf.WriteString("f4")
|
|
|
|
|
buf.WriteString("f4")
|
|
|
|
|
|
|
|
|
|
case types.TCOMPLEX128:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TFLOAT64])
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TFLOAT64])
|
|
|
|
|
buf.WriteString("f8")
|
|
|
|
|
buf.WriteString("f8")
|
|
|
|
|
|
|
|
|
|
case types.TFLOAT32:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TFLOAT32])
|
|
|
|
|
buf.WriteString("f4")
|
|
|
|
|
|
|
|
|
|
case types.TFLOAT64:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TFLOAT64])
|
|
|
|
|
buf.WriteString("f8")
|
|
|
|
|
|
|
|
|
|
case types.TBOOL:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TINT8])
|
|
|
|
|
buf.WriteString("i1")
|
|
|
|
|
|
|
|
|
|
case types.TPTR:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
|
|
|
buf.WriteString("p")
|
|
|
|
|
|
|
|
|
|
case types.TFUNC:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
|
|
|
buf.WriteString("p")
|
|
|
|
|
|
|
|
|
|
case types.TSLICE:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
|
|
|
fl = addGcType(fl, types.Types[INTTYPE])
|
|
|
|
|
fl = addGcType(fl, types.Types[INTTYPE])
|
|
|
|
|
buf.WriteString("p")
|
|
|
|
|
buf.WriteString(INTSTRING)
|
|
|
|
|
buf.WriteString(INTSTRING)
|
|
|
|
|
|
|
|
|
|
case types.TARRAY:
|
|
|
|
|
n := t.NumElem()
|
|
|
|
|
if n == 1 {
|
|
|
|
|
fl = accumGcshape(fl, buf, t.Elem(), nil)
|
|
|
|
|
} else if n > 0 {
|
|
|
|
|
// Represent an array with more than one element as its
|
|
|
|
|
// unique type, since it must be treated differently for
|
|
|
|
|
// regabi.
|
|
|
|
|
fl = addGcType(fl, t)
|
|
|
|
|
buf.WriteByte('[')
|
|
|
|
|
buf.WriteString(strconv.Itoa(int(n)))
|
|
|
|
|
buf.WriteString("](")
|
|
|
|
|
var ignore []*types.Field
|
|
|
|
|
// But to determine its gcshape name, we must call
|
|
|
|
|
// accumGcShape() on t.Elem().
|
|
|
|
|
accumGcshape(ignore, buf, t.Elem(), nil)
|
|
|
|
|
buf.WriteByte(')')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case types.TSTRUCT:
|
|
|
|
|
nfields := t.NumFields()
|
|
|
|
|
for i, f := range t.Fields().Slice() {
|
|
|
|
|
fl = accumGcshape(fl, buf, f.Type, f.Sym)
|
|
|
|
|
|
|
|
|
|
// Check if we need to add an alignment field.
|
|
|
|
|
var pad int64
|
|
|
|
|
if i < nfields-1 {
|
|
|
|
|
pad = t.Field(i+1).Offset - f.Offset - f.Type.Width
|
|
|
|
|
} else {
|
|
|
|
|
pad = t.Width - f.Offset - f.Type.Width
|
|
|
|
|
}
|
|
|
|
|
if pad > 0 {
|
|
|
|
|
// There is padding between fields or at end of
|
|
|
|
|
// struct. Add an alignment field.
|
|
|
|
|
fl = addGcType(fl, types.NewArray(types.Types[types.TUINT8], pad))
|
|
|
|
|
buf.WriteString("a")
|
|
|
|
|
buf.WriteString(strconv.Itoa(int(pad)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case types.TCHAN:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
|
|
|
buf.WriteString("p")
|
|
|
|
|
|
|
|
|
|
case types.TMAP:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
|
|
|
buf.WriteString("p")
|
|
|
|
|
|
|
|
|
|
case types.TINTER:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
|
|
|
buf.WriteString("pp")
|
|
|
|
|
|
|
|
|
|
case types.TFORW, types.TANY:
|
|
|
|
|
assert(false)
|
|
|
|
|
|
|
|
|
|
case types.TSTRING:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
|
|
|
fl = addGcType(fl, types.Types[INTTYPE])
|
|
|
|
|
buf.WriteString("p")
|
|
|
|
|
buf.WriteString(INTSTRING)
|
|
|
|
|
|
|
|
|
|
case types.TUNSAFEPTR:
|
|
|
|
|
fl = addGcType(fl, types.Types[types.TUNSAFEPTR])
|
|
|
|
|
buf.WriteString("p")
|
|
|
|
|
|
|
|
|
|
default: // Everything TTYPEPARAM and below in list of Kinds
|
|
|
|
|
assert(false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fl
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// gcshapeType returns the GCshape type and name corresponding to type t.
|
|
|
|
|
func gcshapeType(t *types.Type) (*types.Type, string) {
|
|
|
|
|
var fl []*types.Field
|
|
|
|
|
buf := bytes.NewBufferString("")
|
|
|
|
|
|
|
|
|
|
// Call CallSize so type sizes and field offsets are available.
|
|
|
|
|
types.CalcSize(t)
|
2021-07-09 16:27:22 -07:00
|
|
|
|
|
|
|
|
instType := t.Sym() != nil && t.IsFullyInstantiated()
|
|
|
|
|
if instType {
|
|
|
|
|
// We distinguish the gcshape of all top-level instantiated type from
|
|
|
|
|
// normal concrete types, even if they have the exact same underlying
|
|
|
|
|
// "shape", because in a function instantiation, any method call on
|
|
|
|
|
// this type arg will be a generic method call (requiring a
|
|
|
|
|
// dictionary), rather than a direct method call on the underlying
|
|
|
|
|
// type (no dictionary). So, we add the instshape prefix to the
|
|
|
|
|
// normal gcshape name, and will make it a defined type with that
|
|
|
|
|
// name below.
|
|
|
|
|
buf.WriteString("instshape-")
|
|
|
|
|
}
|
2021-06-21 17:04:59 -07:00
|
|
|
fl = accumGcshape(fl, buf, t, nil)
|
2021-07-09 16:27:22 -07:00
|
|
|
|
2021-06-21 17:04:59 -07:00
|
|
|
// TODO: Should gcshapes be in a global package, so we don't have to
|
|
|
|
|
// duplicate in each package? Or at least in the specified source package
|
|
|
|
|
// of a function/method instantiation?
|
|
|
|
|
gcshape := types.NewStruct(types.LocalPkg, fl)
|
2021-07-09 16:27:22 -07:00
|
|
|
gcname := buf.String()
|
|
|
|
|
if instType {
|
|
|
|
|
// Lookup or create type with name 'gcname' (with instshape prefix).
|
|
|
|
|
newsym := t.Sym().Pkg.Lookup(gcname)
|
|
|
|
|
if newsym.Def != nil {
|
|
|
|
|
gcshape = newsym.Def.Type()
|
|
|
|
|
} else {
|
|
|
|
|
newt := typecheck.NewIncompleteNamedType(t.Pos(), newsym)
|
|
|
|
|
newt.SetUnderlying(gcshape.Underlying())
|
|
|
|
|
gcshape = newt
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-21 17:04:59 -07:00
|
|
|
assert(gcshape.Size() == t.Size())
|
|
|
|
|
return gcshape, buf.String()
|
|
|
|
|
}
|
|
|
|
|
|
[dev.typeparams] Add optional sub-dict entry for typeparam bound calls
In the case that a generic function/method f does a method call on a
type param allowed by its bound, an instantiation of f may do a direct
method call of a concrete type or a method call defined on a generic
type, depending on whether the passed type in a concrete type or an
instantiated type with the appropriate method defined. See the test case
boundmethod.go added to this change.
In order to keep the dictionary format the same for all instantiations
of a generic function/method, I decided to have an optional
sub-dictionary entry for "bounds" calls. At the point that we are
creating the actual dictionary, we can then fill in the needed
sub-dictionary, if the type arg is an instantiated type, or a zeroed
dictionary entry, if type arg is not instantiated and the method will be
on a concrete type.
In order to implement this, I now fill in n.Selection for "bounds"
method calls in generic functions as well. Also, I need to calculate
n.Selection correctly during import for the case where it is now set -
method calls on generic types, and bounds calls on typeparams.
With this change, the dictionaries/sub-dictionaries are correct for
absdiff.go. The new test boundmethod.go illustrates the case where the
bound sub-dict entry is not used for a dictionary for stringify[myint],
but is used for a dictionary for stringify[StringInt[myint]].
Change-Id: Ie2bcb971b7019a9f1da68c97eb03da2333327457
Reviewed-on: https://go-review.googlesource.com/c/go/+/333456
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
2021-07-02 17:51:20 -07:00
|
|
|
// checkFetchBody checks if a generic body can be fetched, but hasn't been loaded
|
|
|
|
|
// yet. If so, it imports the body.
|
|
|
|
|
func checkFetchBody(nameNode *ir.Name) {
|
[dev.typeparams] cmd/compile: get export/import of generic types & functions working
The general idea is that we now export/import typeparams, typeparam
lists for generic types and functions, and instantiated types
(instantiations of generic types with either new typeparams or concrete
types).
This changes the export format -- the next CL in the stack adds the
export versions and checks for it in the appropriate places.
We always export/import generic function bodies, using the same code
that we use for exporting/importing the bodies of inlineable functions.
To avoid complicated scoping, we consider all type params as unique and
give them unique names for types1. We therefore include the types2 ids
(subscripts) in the export format and re-create on import. We always
access the same unique types1 typeParam type for the same typeparam
name.
We create fully-instantiated generic types and functions in the original
source package. We do an extra NeedRuntimeType() call to make sure that
the correct DWARF information is written out. We call SetDupOK(true) for
the functions/methods to have the linker automatically drop duplicate
instantiations.
Other miscellaneous details:
- Export/import of typeparam bounds works for methods (but not
typelists) for now, but will change with the typeset changes.
- Added a new types.Instantiate function roughly analogous to the
types2.Instantiate function recently added.
- Always access methods info from the original/base generic type, since
the methods of an instantiated type are not filled in (in types2 or
types1).
- New field OrigSym in types.Type to keep track of base generic type
that instantiated type was based on. We use the generic type's symbol
(OrigSym) as the link, rather than a Type pointer, since we haven't
always created the base type yet when we want to set the link (during
types2 to types1 conversion).
- Added types2.AsTypeParam(), (*types2.TypeParam).SetId()
- New test minimp.dir, which tests use of generic function Min across
packages. Another test stringimp.dir, which also exports a generic
function Stringify across packages, where the type param has a bound
(Stringer) as well. New test pairimp.dir, which tests use of generic
type Pair (with no methods) across packages.
- New test valimp.dir, which tests use of generic type (with methods
and related functions) across packages.
- Modified several other tests (adder.go, settable.go, smallest.go,
stringable.go, struct.go, sum.go) to export their generic
functions/types to show that generic functions/types can be exported
successfully (but this doesn't test import).
Change-Id: Ie61ce9d54a46d368ddc7a76c41399378963bb57f
Reviewed-on: https://go-review.googlesource.com/c/go/+/319930
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-04-13 15:37:36 -07:00
|
|
|
if nameNode.Func.Body == nil && nameNode.Func.Inl != nil {
|
|
|
|
|
// If there is no body yet but Func.Inl exists, then we can can
|
|
|
|
|
// import the whole generic body.
|
|
|
|
|
assert(nameNode.Func.Inl.Cost == 1 && nameNode.Sym().Pkg != types.LocalPkg)
|
|
|
|
|
typecheck.ImportBody(nameNode.Func)
|
|
|
|
|
assert(nameNode.Func.Inl.Body != nil)
|
|
|
|
|
nameNode.Func.Body = nameNode.Func.Inl.Body
|
|
|
|
|
nameNode.Func.Dcl = nameNode.Func.Inl.Dcl
|
|
|
|
|
}
|
[dev.typeparams] Add optional sub-dict entry for typeparam bound calls
In the case that a generic function/method f does a method call on a
type param allowed by its bound, an instantiation of f may do a direct
method call of a concrete type or a method call defined on a generic
type, depending on whether the passed type in a concrete type or an
instantiated type with the appropriate method defined. See the test case
boundmethod.go added to this change.
In order to keep the dictionary format the same for all instantiations
of a generic function/method, I decided to have an optional
sub-dictionary entry for "bounds" calls. At the point that we are
creating the actual dictionary, we can then fill in the needed
sub-dictionary, if the type arg is an instantiated type, or a zeroed
dictionary entry, if type arg is not instantiated and the method will be
on a concrete type.
In order to implement this, I now fill in n.Selection for "bounds"
method calls in generic functions as well. Also, I need to calculate
n.Selection correctly during import for the case where it is now set -
method calls on generic types, and bounds calls on typeparams.
With this change, the dictionaries/sub-dictionaries are correct for
absdiff.go. The new test boundmethod.go illustrates the case where the
bound sub-dict entry is not used for a dictionary for stringify[myint],
but is used for a dictionary for stringify[StringInt[myint]].
Change-Id: Ie2bcb971b7019a9f1da68c97eb03da2333327457
Reviewed-on: https://go-review.googlesource.com/c/go/+/333456
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
2021-07-02 17:51:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// getInstantiation gets the instantiantion and dictionary of the function or method nameNode
|
|
|
|
|
// with the type arguments targs. If the instantiated function is not already
|
|
|
|
|
// cached, then it calls genericSubst to create the new instantiation.
|
|
|
|
|
func (g *irgen) getInstantiation(nameNode *ir.Name, targs []*types.Type, isMeth bool) *ir.Func {
|
|
|
|
|
checkFetchBody(nameNode)
|
2021-06-09 19:30:16 -07:00
|
|
|
|
|
|
|
|
// Convert type arguments to their shape, so we can reduce the number
|
|
|
|
|
// of instantiations we have to generate.
|
|
|
|
|
shapes := typecheck.ShapifyList(targs)
|
|
|
|
|
|
|
|
|
|
sym := typecheck.MakeInstName(nameNode.Sym(), shapes, isMeth)
|
2021-06-28 18:04:58 -07:00
|
|
|
info := g.instInfoMap[sym]
|
|
|
|
|
if info == nil {
|
2021-06-21 17:04:59 -07:00
|
|
|
if false {
|
|
|
|
|
// Testing out gcshapeType() and gcshapeName()
|
|
|
|
|
for i, t := range targs {
|
|
|
|
|
gct, gcs := gcshapeType(t)
|
2021-07-09 16:27:22 -07:00
|
|
|
fmt.Printf("targ %d: %v %v %v\n", i, gcs, gct, gct.Underlying())
|
2021-06-21 17:04:59 -07:00
|
|
|
}
|
|
|
|
|
}
|
2021-02-21 10:54:38 -08:00
|
|
|
// If instantiation doesn't exist yet, create it and add
|
|
|
|
|
// to the list of decls.
|
2021-06-28 18:04:58 -07:00
|
|
|
gfInfo := g.getGfInfo(nameNode)
|
|
|
|
|
info = &instInfo{
|
[dev.typeparams] cmd/compile: add dictionary entries for itab conversion
This fix the case where a type param or derived type is converted to a
non-empty interface. Previously, we were converting to an empty
interface and then using DOTTYPE to convert to the correct non-empty
interface. In that case, we can get the needed itab directly from the
dictionary. This is needed for correctness from shapes, if the
destination interface is parameterized, else we will incorrectly convert
to the shape version of the interface.
Creating/writing an itab can involve generating wrappers for a bunch of
methods, which may use dictionaries. So, all the
dictionaries/instantiations are being generated on the fly and have
recursive relationships, it is simplest to finish creating/writing the
itabs at the end of the stenciling phase. So, we create a list of the
dictionaries which need to be completed by writing out their itab
entries.
The existing tests ordered.go, ifaceconv.go, and issue44688.go make use
of this optimization.
Got itab conversions for bound calls working, except for 13.go.
Also, want to get rid of the concretify, but I think we need more info
on the Bound from types2.
Change-Id: If552958a7b8a435500d6cc42c401572c367b30d1
Reviewed-on: https://go-review.googlesource.com/c/go/+/336993
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2021-07-12 19:34:15 -07:00
|
|
|
gf: nameNode,
|
|
|
|
|
gfInfo: gfInfo,
|
|
|
|
|
startSubDict: len(targs) + len(gfInfo.derivedTypes),
|
|
|
|
|
startItabConv: len(targs) + len(gfInfo.derivedTypes) + len(gfInfo.subDictCalls),
|
|
|
|
|
dictLen: len(targs) + len(gfInfo.derivedTypes) + len(gfInfo.subDictCalls) + len(gfInfo.itabConvs),
|
|
|
|
|
dictEntryMap: make(map[ir.Node]int),
|
2021-06-28 18:04:58 -07:00
|
|
|
}
|
|
|
|
|
// genericSubst fills in info.dictParam and info.dictEntryMap.
|
2021-06-09 19:30:16 -07:00
|
|
|
st := g.genericSubst(sym, nameNode, shapes, targs, isMeth, info)
|
2021-06-28 18:04:58 -07:00
|
|
|
info.fun = st
|
|
|
|
|
g.instInfoMap[sym] = info
|
[dev.typeparams] cmd/compile: get export/import of generic types & functions working
The general idea is that we now export/import typeparams, typeparam
lists for generic types and functions, and instantiated types
(instantiations of generic types with either new typeparams or concrete
types).
This changes the export format -- the next CL in the stack adds the
export versions and checks for it in the appropriate places.
We always export/import generic function bodies, using the same code
that we use for exporting/importing the bodies of inlineable functions.
To avoid complicated scoping, we consider all type params as unique and
give them unique names for types1. We therefore include the types2 ids
(subscripts) in the export format and re-create on import. We always
access the same unique types1 typeParam type for the same typeparam
name.
We create fully-instantiated generic types and functions in the original
source package. We do an extra NeedRuntimeType() call to make sure that
the correct DWARF information is written out. We call SetDupOK(true) for
the functions/methods to have the linker automatically drop duplicate
instantiations.
Other miscellaneous details:
- Export/import of typeparam bounds works for methods (but not
typelists) for now, but will change with the typeset changes.
- Added a new types.Instantiate function roughly analogous to the
types2.Instantiate function recently added.
- Always access methods info from the original/base generic type, since
the methods of an instantiated type are not filled in (in types2 or
types1).
- New field OrigSym in types.Type to keep track of base generic type
that instantiated type was based on. We use the generic type's symbol
(OrigSym) as the link, rather than a Type pointer, since we haven't
always created the base type yet when we want to set the link (during
types2 to types1 conversion).
- Added types2.AsTypeParam(), (*types2.TypeParam).SetId()
- New test minimp.dir, which tests use of generic function Min across
packages. Another test stringimp.dir, which also exports a generic
function Stringify across packages, where the type param has a bound
(Stringer) as well. New test pairimp.dir, which tests use of generic
type Pair (with no methods) across packages.
- New test valimp.dir, which tests use of generic type (with methods
and related functions) across packages.
- Modified several other tests (adder.go, settable.go, smallest.go,
stringable.go, struct.go, sum.go) to export their generic
functions/types to show that generic functions/types can be exported
successfully (but this doesn't test import).
Change-Id: Ie61ce9d54a46d368ddc7a76c41399378963bb57f
Reviewed-on: https://go-review.googlesource.com/c/go/+/319930
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-04-13 15:37:36 -07:00
|
|
|
// This ensures that the linker drops duplicates of this instantiation.
|
|
|
|
|
// All just works!
|
|
|
|
|
st.SetDupok(true)
|
2021-02-21 10:54:38 -08:00
|
|
|
g.target.Decls = append(g.target.Decls, st)
|
|
|
|
|
if base.Flag.W > 1 {
|
|
|
|
|
ir.Dump(fmt.Sprintf("\nstenciled %v", st), st)
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-02 12:32:38 -07:00
|
|
|
return info.fun
|
2021-02-21 10:54:38 -08:00
|
|
|
}
|
|
|
|
|
|
2021-02-03 15:45:26 -08:00
|
|
|
// Struct containing info needed for doing the substitution as we create the
|
|
|
|
|
// instantiation of a generic function with specified type arguments.
|
|
|
|
|
type subster struct {
|
2021-06-28 18:04:58 -07:00
|
|
|
g *irgen
|
|
|
|
|
isMethod bool // If a method is being instantiated
|
|
|
|
|
newf *ir.Func // Func node for the new stenciled function
|
|
|
|
|
ts typecheck.Tsubster
|
|
|
|
|
info *instInfo // Place to put extra info in the instantiation
|
2021-06-09 19:30:16 -07:00
|
|
|
|
|
|
|
|
// unshapeify maps from shape types to the concrete types they represent.
|
|
|
|
|
// TODO: remove when we no longer need it.
|
2021-07-23 17:19:51 -07:00
|
|
|
unshapify typecheck.Tsubster
|
2021-06-09 19:30:16 -07:00
|
|
|
|
|
|
|
|
// TODO: some sort of map from <shape type, interface type> to index in the
|
|
|
|
|
// dictionary where a *runtime.itab for the corresponding <concrete type,
|
|
|
|
|
// interface type> pair resides.
|
2021-02-03 15:45:26 -08:00
|
|
|
}
|
|
|
|
|
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
// genericSubst returns a new function with name newsym. The function is an
|
|
|
|
|
// instantiation of a generic function or method specified by namedNode with type
|
|
|
|
|
// args targs. For a method with a generic receiver, it returns an instantiated
|
|
|
|
|
// function type where the receiver becomes the first parameter. Otherwise the
|
|
|
|
|
// instantiated method would still need to be transformed by later compiler
|
2021-06-28 18:04:58 -07:00
|
|
|
// phases. genericSubst fills in info.dictParam and info.dictEntryMap.
|
2021-06-09 19:30:16 -07:00
|
|
|
func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, shapes, targs []*types.Type, isMethod bool, info *instInfo) *ir.Func {
|
2021-05-19 10:04:44 -07:00
|
|
|
var tparams []*types.Type
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
if isMethod {
|
2021-02-11 10:50:20 -08:00
|
|
|
// Get the type params from the method receiver (after skipping
|
|
|
|
|
// over any pointer)
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
recvType := nameNode.Type().Recv().Type
|
|
|
|
|
recvType = deref(recvType)
|
2021-05-19 10:04:44 -07:00
|
|
|
tparams = recvType.RParams()
|
2021-02-11 10:50:20 -08:00
|
|
|
} else {
|
2021-05-19 10:04:44 -07:00
|
|
|
fields := nameNode.Type().TParams().Fields().Slice()
|
|
|
|
|
tparams = make([]*types.Type, len(fields))
|
|
|
|
|
for i, f := range fields {
|
|
|
|
|
tparams[i] = f.Type
|
|
|
|
|
}
|
2021-02-11 10:50:20 -08:00
|
|
|
}
|
2021-06-09 19:30:16 -07:00
|
|
|
for i := range targs {
|
|
|
|
|
if targs[i].HasShape() {
|
|
|
|
|
base.Fatalf("generiSubst shape %s %+v %+v\n", newsym.Name, shapes[i], targs[i])
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-02-03 15:45:26 -08:00
|
|
|
gf := nameNode.Func
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
// Pos of the instantiated function is same as the generic function
|
|
|
|
|
newf := ir.NewFunc(gf.Pos())
|
2021-04-14 16:18:58 -07:00
|
|
|
newf.Pragma = gf.Pragma // copy over pragmas from generic function to stenciled implementation.
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
newf.Nname = ir.NewNameAt(gf.Pos(), newsym)
|
2021-02-03 15:45:26 -08:00
|
|
|
newf.Nname.Func = newf
|
|
|
|
|
newf.Nname.Defn = newf
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
newsym.Def = newf.Nname
|
2021-04-26 17:52:53 -07:00
|
|
|
savef := ir.CurFunc
|
|
|
|
|
// transformCall/transformReturn (called during stenciling of the body)
|
|
|
|
|
// depend on ir.CurFunc being set.
|
2021-03-29 08:28:01 -07:00
|
|
|
ir.CurFunc = newf
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
|
2021-06-09 19:30:16 -07:00
|
|
|
assert(len(tparams) == len(shapes))
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
assert(len(tparams) == len(targs))
|
2021-02-03 15:45:26 -08:00
|
|
|
|
|
|
|
|
subst := &subster{
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
g: g,
|
|
|
|
|
isMethod: isMethod,
|
|
|
|
|
newf: newf,
|
2021-06-28 18:04:58 -07:00
|
|
|
info: info,
|
[dev.typeparams] cmd/compile: export/import of recursive generic types.
Deal with export/import of recursive generic types. This includes
typeparams which have bounds that reference the typeparam.
There are three main changes:
- Change export/import of typeparams to have an implicit "declaration"
(doDecl). We need to do a declaration of typeparams (via the
typeparam's package and unique name), because it may be referenced
within its bound during its own definition.
- We delay most of the processing of the Instantiate call until we
finish the creation of the top-most type (similar to the way we
delay CheckSize). This is because we can't do the full instantiation
properly until the base type is fully defined (with methods). The
functions delayDoInst() and resumeDoInst() delay and resume the
processing of the instantiations.
- To do the full needed type substitutions for type instantiations
during import, I had to separate out the type subster in stencil.go
and move it to subr.go in the typecheck package. The subster in
stencil.go now does node substitution and makes use of the type
subster to do type substitutions.
Notable other changes:
- In types/builtins.go, put the newly defined typeparam for a union type
(related to use of real/imag, etc.) in the current package, rather
than the builtin package, so exports/imports work properly.
- In types2, allowed NewTypeParam() to be called with a nil bound, and
allow setting the bound later. (Needed to import a typeparam whose
bound refers to the typeparam itself.)
- During import of typeparams in types2 (importer/import.go), we need
to keep an index of the typeparams by their package and unique name
(with id). Use a new map typParamIndex[] for that. Again, this is
needed to deal with typeparams whose bounds refer to the typeparam
itself.
- Added several new tests absdiffimp.go and orderedmapsimp.go. Some of
the orderemapsimp tests are commented out for now, because there are
some issues with closures inside instantiations (relating to unexported
names of closure structs).
- Renamed some typeparams in test value.go to make them all T (to make
typeparam uniqueness is working fine).
Change-Id: Ib47ed9471c19ee8e9fbb34e8506907dad3021e5a
Reviewed-on: https://go-review.googlesource.com/c/go/+/323029
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-05-17 15:00:39 -07:00
|
|
|
ts: typecheck.Tsubster{
|
|
|
|
|
Tparams: tparams,
|
2021-06-09 19:30:16 -07:00
|
|
|
Targs: shapes,
|
|
|
|
|
Vars: make(map[*ir.Name]*ir.Name),
|
|
|
|
|
},
|
|
|
|
|
unshapify: typecheck.Tsubster{
|
|
|
|
|
Tparams: shapes,
|
[dev.typeparams] cmd/compile: export/import of recursive generic types.
Deal with export/import of recursive generic types. This includes
typeparams which have bounds that reference the typeparam.
There are three main changes:
- Change export/import of typeparams to have an implicit "declaration"
(doDecl). We need to do a declaration of typeparams (via the
typeparam's package and unique name), because it may be referenced
within its bound during its own definition.
- We delay most of the processing of the Instantiate call until we
finish the creation of the top-most type (similar to the way we
delay CheckSize). This is because we can't do the full instantiation
properly until the base type is fully defined (with methods). The
functions delayDoInst() and resumeDoInst() delay and resume the
processing of the instantiations.
- To do the full needed type substitutions for type instantiations
during import, I had to separate out the type subster in stencil.go
and move it to subr.go in the typecheck package. The subster in
stencil.go now does node substitution and makes use of the type
subster to do type substitutions.
Notable other changes:
- In types/builtins.go, put the newly defined typeparam for a union type
(related to use of real/imag, etc.) in the current package, rather
than the builtin package, so exports/imports work properly.
- In types2, allowed NewTypeParam() to be called with a nil bound, and
allow setting the bound later. (Needed to import a typeparam whose
bound refers to the typeparam itself.)
- During import of typeparams in types2 (importer/import.go), we need
to keep an index of the typeparams by their package and unique name
(with id). Use a new map typParamIndex[] for that. Again, this is
needed to deal with typeparams whose bounds refer to the typeparam
itself.
- Added several new tests absdiffimp.go and orderedmapsimp.go. Some of
the orderemapsimp tests are commented out for now, because there are
some issues with closures inside instantiations (relating to unexported
names of closure structs).
- Renamed some typeparams in test value.go to make them all T (to make
typeparam uniqueness is working fine).
Change-Id: Ib47ed9471c19ee8e9fbb34e8506907dad3021e5a
Reviewed-on: https://go-review.googlesource.com/c/go/+/323029
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-05-17 15:00:39 -07:00
|
|
|
Targs: targs,
|
|
|
|
|
Vars: make(map[*ir.Name]*ir.Name),
|
|
|
|
|
},
|
2021-02-03 15:45:26 -08:00
|
|
|
}
|
|
|
|
|
|
2021-04-16 14:06:50 -07:00
|
|
|
newf.Dcl = make([]*ir.Name, 0, len(gf.Dcl)+1)
|
2021-02-03 15:45:26 -08:00
|
|
|
|
2021-06-28 18:04:58 -07:00
|
|
|
// Create the needed dictionary param
|
2021-06-04 18:17:49 -07:00
|
|
|
dictionarySym := newsym.Pkg.Lookup(".dict")
|
2021-04-16 14:06:50 -07:00
|
|
|
dictionaryType := types.Types[types.TUINTPTR]
|
|
|
|
|
dictionaryName := ir.NewNameAt(gf.Pos(), dictionarySym)
|
|
|
|
|
typed(dictionaryType, dictionaryName)
|
|
|
|
|
dictionaryName.Class = ir.PPARAM
|
|
|
|
|
dictionaryName.Curfn = newf
|
|
|
|
|
newf.Dcl = append(newf.Dcl, dictionaryName)
|
|
|
|
|
for _, n := range gf.Dcl {
|
|
|
|
|
if n.Sym().Name == ".dict" {
|
|
|
|
|
panic("already has dictionary")
|
|
|
|
|
}
|
|
|
|
|
newf.Dcl = append(newf.Dcl, subst.localvar(n))
|
|
|
|
|
}
|
|
|
|
|
dictionaryArg := types.NewField(gf.Pos(), dictionarySym, dictionaryType)
|
|
|
|
|
dictionaryArg.Nname = dictionaryName
|
2021-06-28 18:04:58 -07:00
|
|
|
info.dictParam = dictionaryName
|
|
|
|
|
|
|
|
|
|
// We add the dictionary as the first parameter in the function signature.
|
|
|
|
|
// We also transform a method type to the corresponding function type
|
|
|
|
|
// (make the receiver be the next parameter after the dictionary).
|
|
|
|
|
oldt := nameNode.Type()
|
2021-04-16 14:06:50 -07:00
|
|
|
var args []*types.Field
|
|
|
|
|
args = append(args, dictionaryArg)
|
|
|
|
|
args = append(args, oldt.Recvs().FieldSlice()...)
|
|
|
|
|
args = append(args, oldt.Params().FieldSlice()...)
|
2021-06-28 18:04:58 -07:00
|
|
|
|
|
|
|
|
// Replace the types in the function signature via subst.fields.
|
|
|
|
|
// Ugly: also, we have to insert the Name nodes of the parameters/results into
|
|
|
|
|
// the function type. The current function type has no Nname fields set,
|
|
|
|
|
// because it came via conversion from the types2 type.
|
2021-05-20 15:35:55 -07:00
|
|
|
newt := types.NewSignature(oldt.Pkg(), nil, nil,
|
2021-04-16 14:06:50 -07:00
|
|
|
subst.fields(ir.PPARAM, args, newf.Dcl),
|
2021-05-20 15:35:55 -07:00
|
|
|
subst.fields(ir.PPARAMOUT, oldt.Results().FieldSlice(), newf.Dcl))
|
2021-02-03 15:45:26 -08:00
|
|
|
|
2021-04-16 14:06:50 -07:00
|
|
|
typed(newt, newf.Nname)
|
2021-02-03 15:45:26 -08:00
|
|
|
ir.MarkFunc(newf.Nname)
|
|
|
|
|
newf.SetTypecheck(1)
|
2021-03-29 08:28:01 -07:00
|
|
|
|
|
|
|
|
// Make sure name/type of newf is set before substituting the body.
|
|
|
|
|
newf.Body = subst.list(gf.Body)
|
2021-04-16 14:06:50 -07:00
|
|
|
|
|
|
|
|
// Add code to check that the dictionary is correct.
|
2021-06-09 19:30:16 -07:00
|
|
|
// TODO: must go away when we move to many->1 shape to concrete mapping.
|
|
|
|
|
newf.Body.Prepend(subst.checkDictionary(dictionaryName, targs)...)
|
2021-04-16 14:06:50 -07:00
|
|
|
|
2021-04-26 17:52:53 -07:00
|
|
|
ir.CurFunc = savef
|
[dev.typeparams] cmd/compile: export/import of recursive generic types.
Deal with export/import of recursive generic types. This includes
typeparams which have bounds that reference the typeparam.
There are three main changes:
- Change export/import of typeparams to have an implicit "declaration"
(doDecl). We need to do a declaration of typeparams (via the
typeparam's package and unique name), because it may be referenced
within its bound during its own definition.
- We delay most of the processing of the Instantiate call until we
finish the creation of the top-most type (similar to the way we
delay CheckSize). This is because we can't do the full instantiation
properly until the base type is fully defined (with methods). The
functions delayDoInst() and resumeDoInst() delay and resume the
processing of the instantiations.
- To do the full needed type substitutions for type instantiations
during import, I had to separate out the type subster in stencil.go
and move it to subr.go in the typecheck package. The subster in
stencil.go now does node substitution and makes use of the type
subster to do type substitutions.
Notable other changes:
- In types/builtins.go, put the newly defined typeparam for a union type
(related to use of real/imag, etc.) in the current package, rather
than the builtin package, so exports/imports work properly.
- In types2, allowed NewTypeParam() to be called with a nil bound, and
allow setting the bound later. (Needed to import a typeparam whose
bound refers to the typeparam itself.)
- During import of typeparams in types2 (importer/import.go), we need
to keep an index of the typeparams by their package and unique name
(with id). Use a new map typParamIndex[] for that. Again, this is
needed to deal with typeparams whose bounds refer to the typeparam
itself.
- Added several new tests absdiffimp.go and orderedmapsimp.go. Some of
the orderemapsimp tests are commented out for now, because there are
some issues with closures inside instantiations (relating to unexported
names of closure structs).
- Renamed some typeparams in test value.go to make them all T (to make
typeparam uniqueness is working fine).
Change-Id: Ib47ed9471c19ee8e9fbb34e8506907dad3021e5a
Reviewed-on: https://go-review.googlesource.com/c/go/+/323029
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-05-17 15:00:39 -07:00
|
|
|
// Add any new, fully instantiated types seen during the substitution to
|
|
|
|
|
// g.instTypeList.
|
|
|
|
|
g.instTypeList = append(g.instTypeList, subst.ts.InstTypeList...)
|
2021-06-09 19:30:16 -07:00
|
|
|
g.instTypeList = append(g.instTypeList, subst.unshapify.InstTypeList...)
|
2021-03-29 08:28:01 -07:00
|
|
|
|
2021-07-23 15:23:57 -07:00
|
|
|
if doubleCheck {
|
|
|
|
|
okConvs := map[ir.Node]bool{}
|
|
|
|
|
ir.Visit(newf, func(n ir.Node) {
|
|
|
|
|
if n.Op() == ir.OIDATA {
|
|
|
|
|
// IDATA(OCONVIFACE(x)) is ok, as we don't use the type of x.
|
|
|
|
|
// TODO: use some other op besides OCONVIFACE. ONEW might work
|
|
|
|
|
// (with appropriate direct vs. indirect interface cases).
|
|
|
|
|
okConvs[n.(*ir.UnaryExpr).X] = true
|
|
|
|
|
}
|
|
|
|
|
if n.Op() == ir.OCONVIFACE && !okConvs[n] {
|
|
|
|
|
c := n.(*ir.ConvExpr)
|
|
|
|
|
if c.X.Type().HasShape() {
|
|
|
|
|
ir.Dump("BAD FUNCTION", newf)
|
|
|
|
|
ir.Dump("BAD CONVERSION", c)
|
|
|
|
|
base.Fatalf("converting shape type to interface")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-03 15:45:26 -08:00
|
|
|
return newf
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-09 19:30:16 -07:00
|
|
|
func (subst *subster) unshapifyTyp(t *types.Type) *types.Type {
|
|
|
|
|
res := subst.unshapify.Typ(t)
|
|
|
|
|
types.CheckSize(res)
|
|
|
|
|
return res
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-11 19:29:10 -07:00
|
|
|
// localvar creates a new name node for the specified local variable and enters it
|
|
|
|
|
// in subst.vars. It substitutes type arguments for type parameters in the type of
|
|
|
|
|
// name as needed.
|
|
|
|
|
func (subst *subster) localvar(name *ir.Name) *ir.Name {
|
|
|
|
|
m := ir.NewNameAt(name.Pos(), name.Sym())
|
|
|
|
|
if name.IsClosureVar() {
|
|
|
|
|
m.SetIsClosureVar(true)
|
|
|
|
|
}
|
[dev.typeparams] cmd/compile: export/import of recursive generic types.
Deal with export/import of recursive generic types. This includes
typeparams which have bounds that reference the typeparam.
There are three main changes:
- Change export/import of typeparams to have an implicit "declaration"
(doDecl). We need to do a declaration of typeparams (via the
typeparam's package and unique name), because it may be referenced
within its bound during its own definition.
- We delay most of the processing of the Instantiate call until we
finish the creation of the top-most type (similar to the way we
delay CheckSize). This is because we can't do the full instantiation
properly until the base type is fully defined (with methods). The
functions delayDoInst() and resumeDoInst() delay and resume the
processing of the instantiations.
- To do the full needed type substitutions for type instantiations
during import, I had to separate out the type subster in stencil.go
and move it to subr.go in the typecheck package. The subster in
stencil.go now does node substitution and makes use of the type
subster to do type substitutions.
Notable other changes:
- In types/builtins.go, put the newly defined typeparam for a union type
(related to use of real/imag, etc.) in the current package, rather
than the builtin package, so exports/imports work properly.
- In types2, allowed NewTypeParam() to be called with a nil bound, and
allow setting the bound later. (Needed to import a typeparam whose
bound refers to the typeparam itself.)
- During import of typeparams in types2 (importer/import.go), we need
to keep an index of the typeparams by their package and unique name
(with id). Use a new map typParamIndex[] for that. Again, this is
needed to deal with typeparams whose bounds refer to the typeparam
itself.
- Added several new tests absdiffimp.go and orderedmapsimp.go. Some of
the orderemapsimp tests are commented out for now, because there are
some issues with closures inside instantiations (relating to unexported
names of closure structs).
- Renamed some typeparams in test value.go to make them all T (to make
typeparam uniqueness is working fine).
Change-Id: Ib47ed9471c19ee8e9fbb34e8506907dad3021e5a
Reviewed-on: https://go-review.googlesource.com/c/go/+/323029
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-05-17 15:00:39 -07:00
|
|
|
m.SetType(subst.ts.Typ(name.Type()))
|
2021-05-11 19:29:10 -07:00
|
|
|
m.BuiltinOp = name.BuiltinOp
|
|
|
|
|
m.Curfn = subst.newf
|
|
|
|
|
m.Class = name.Class
|
|
|
|
|
assert(name.Class != ir.PEXTERN && name.Class != ir.PFUNC)
|
|
|
|
|
m.Func = name.Func
|
[dev.typeparams] cmd/compile: export/import of recursive generic types.
Deal with export/import of recursive generic types. This includes
typeparams which have bounds that reference the typeparam.
There are three main changes:
- Change export/import of typeparams to have an implicit "declaration"
(doDecl). We need to do a declaration of typeparams (via the
typeparam's package and unique name), because it may be referenced
within its bound during its own definition.
- We delay most of the processing of the Instantiate call until we
finish the creation of the top-most type (similar to the way we
delay CheckSize). This is because we can't do the full instantiation
properly until the base type is fully defined (with methods). The
functions delayDoInst() and resumeDoInst() delay and resume the
processing of the instantiations.
- To do the full needed type substitutions for type instantiations
during import, I had to separate out the type subster in stencil.go
and move it to subr.go in the typecheck package. The subster in
stencil.go now does node substitution and makes use of the type
subster to do type substitutions.
Notable other changes:
- In types/builtins.go, put the newly defined typeparam for a union type
(related to use of real/imag, etc.) in the current package, rather
than the builtin package, so exports/imports work properly.
- In types2, allowed NewTypeParam() to be called with a nil bound, and
allow setting the bound later. (Needed to import a typeparam whose
bound refers to the typeparam itself.)
- During import of typeparams in types2 (importer/import.go), we need
to keep an index of the typeparams by their package and unique name
(with id). Use a new map typParamIndex[] for that. Again, this is
needed to deal with typeparams whose bounds refer to the typeparam
itself.
- Added several new tests absdiffimp.go and orderedmapsimp.go. Some of
the orderemapsimp tests are commented out for now, because there are
some issues with closures inside instantiations (relating to unexported
names of closure structs).
- Renamed some typeparams in test value.go to make them all T (to make
typeparam uniqueness is working fine).
Change-Id: Ib47ed9471c19ee8e9fbb34e8506907dad3021e5a
Reviewed-on: https://go-review.googlesource.com/c/go/+/323029
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-05-17 15:00:39 -07:00
|
|
|
subst.ts.Vars[name] = m
|
2021-05-11 19:29:10 -07:00
|
|
|
m.SetTypecheck(1)
|
|
|
|
|
return m
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-16 14:06:50 -07:00
|
|
|
// checkDictionary returns code that does runtime consistency checks
|
|
|
|
|
// between the dictionary and the types it should contain.
|
2021-06-09 19:30:16 -07:00
|
|
|
func (subst *subster) checkDictionary(name *ir.Name, targs []*types.Type) (code []ir.Node) {
|
2021-04-16 14:06:50 -07:00
|
|
|
if false {
|
|
|
|
|
return // checking turned off
|
|
|
|
|
}
|
|
|
|
|
// TODO: when moving to GCshape, this test will become harder. Call into
|
|
|
|
|
// runtime to check the expected shape is correct?
|
|
|
|
|
pos := name.Pos()
|
|
|
|
|
// Convert dictionary to *[N]uintptr
|
|
|
|
|
d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], name)
|
|
|
|
|
d.SetTypecheck(1)
|
|
|
|
|
d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(len(targs))).PtrTo(), d)
|
|
|
|
|
d.SetTypecheck(1)
|
|
|
|
|
|
|
|
|
|
// Check that each type entry in the dictionary is correct.
|
|
|
|
|
for i, t := range targs {
|
2021-06-09 19:30:16 -07:00
|
|
|
if t.HasShape() {
|
|
|
|
|
// Check the concrete type, not the shape type.
|
|
|
|
|
// TODO: can this happen?
|
|
|
|
|
//t = subst.unshapify.Typ(t)
|
|
|
|
|
base.Fatalf("shape type in dictionary %s %+v\n", name.Sym().Name, t)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2021-04-16 14:06:50 -07:00
|
|
|
want := reflectdata.TypePtr(t)
|
|
|
|
|
typed(types.Types[types.TUINTPTR], want)
|
|
|
|
|
deref := ir.NewStarExpr(pos, d)
|
|
|
|
|
typed(d.Type().Elem(), deref)
|
|
|
|
|
idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), name) // TODO: what to set orig to?
|
|
|
|
|
typed(types.Types[types.TUINTPTR], idx)
|
|
|
|
|
got := ir.NewIndexExpr(pos, deref, idx)
|
|
|
|
|
typed(types.Types[types.TUINTPTR], got)
|
|
|
|
|
cond := ir.NewBinaryExpr(pos, ir.ONE, want, got)
|
|
|
|
|
typed(types.Types[types.TBOOL], cond)
|
|
|
|
|
panicArg := ir.NewNilExpr(pos)
|
|
|
|
|
typed(types.NewInterface(types.LocalPkg, nil), panicArg)
|
|
|
|
|
then := ir.NewUnaryExpr(pos, ir.OPANIC, panicArg)
|
|
|
|
|
then.SetTypecheck(1)
|
|
|
|
|
x := ir.NewIfStmt(pos, cond, []ir.Node{then}, nil)
|
|
|
|
|
x.SetTypecheck(1)
|
|
|
|
|
code = append(code, x)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-28 18:04:58 -07:00
|
|
|
// getDictionaryEntry gets the i'th entry in the dictionary dict.
|
|
|
|
|
func getDictionaryEntry(pos src.XPos, dict *ir.Name, i int, size int) ir.Node {
|
|
|
|
|
// Convert dictionary to *[N]uintptr
|
|
|
|
|
// All entries in the dictionary are pointers. They all point to static data, though, so we
|
|
|
|
|
// treat them as uintptrs so the GC doesn't need to keep track of them.
|
|
|
|
|
d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], dict)
|
|
|
|
|
d.SetTypecheck(1)
|
|
|
|
|
d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(size)).PtrTo(), d)
|
|
|
|
|
d.SetTypecheck(1)
|
|
|
|
|
|
|
|
|
|
// Load entry i out of the dictionary.
|
|
|
|
|
deref := ir.NewStarExpr(pos, d)
|
|
|
|
|
typed(d.Type().Elem(), deref)
|
|
|
|
|
idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), dict) // TODO: what to set orig to?
|
|
|
|
|
typed(types.Types[types.TUINTPTR], idx)
|
|
|
|
|
r := ir.NewIndexExpr(pos, deref, idx)
|
|
|
|
|
typed(types.Types[types.TUINTPTR], r)
|
|
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-06 09:38:58 -07:00
|
|
|
// getDictionaryType returns a *runtime._type from the dictionary entry i
|
|
|
|
|
// (which refers to a type param or a derived type that uses type params).
|
|
|
|
|
func (subst *subster) getDictionaryType(pos src.XPos, i int) ir.Node {
|
|
|
|
|
if i < 0 || i >= subst.info.startSubDict {
|
|
|
|
|
base.Fatalf(fmt.Sprintf("bad dict index %d", i))
|
2021-06-04 17:19:09 -07:00
|
|
|
}
|
|
|
|
|
|
2021-07-06 09:38:58 -07:00
|
|
|
r := getDictionaryEntry(pos, subst.info.dictParam, i, subst.info.startSubDict)
|
2021-06-28 18:04:58 -07:00
|
|
|
// change type of retrieved dictionary entry to *byte, which is the
|
|
|
|
|
// standard typing of a *runtime._type in the compiler
|
|
|
|
|
typed(types.Types[types.TUINT8].PtrTo(), r)
|
2021-06-04 17:19:09 -07:00
|
|
|
return r
|
|
|
|
|
}
|
|
|
|
|
|
[dev.typeparams] cmd/compile: export/import of recursive generic types.
Deal with export/import of recursive generic types. This includes
typeparams which have bounds that reference the typeparam.
There are three main changes:
- Change export/import of typeparams to have an implicit "declaration"
(doDecl). We need to do a declaration of typeparams (via the
typeparam's package and unique name), because it may be referenced
within its bound during its own definition.
- We delay most of the processing of the Instantiate call until we
finish the creation of the top-most type (similar to the way we
delay CheckSize). This is because we can't do the full instantiation
properly until the base type is fully defined (with methods). The
functions delayDoInst() and resumeDoInst() delay and resume the
processing of the instantiations.
- To do the full needed type substitutions for type instantiations
during import, I had to separate out the type subster in stencil.go
and move it to subr.go in the typecheck package. The subster in
stencil.go now does node substitution and makes use of the type
subster to do type substitutions.
Notable other changes:
- In types/builtins.go, put the newly defined typeparam for a union type
(related to use of real/imag, etc.) in the current package, rather
than the builtin package, so exports/imports work properly.
- In types2, allowed NewTypeParam() to be called with a nil bound, and
allow setting the bound later. (Needed to import a typeparam whose
bound refers to the typeparam itself.)
- During import of typeparams in types2 (importer/import.go), we need
to keep an index of the typeparams by their package and unique name
(with id). Use a new map typParamIndex[] for that. Again, this is
needed to deal with typeparams whose bounds refer to the typeparam
itself.
- Added several new tests absdiffimp.go and orderedmapsimp.go. Some of
the orderemapsimp tests are commented out for now, because there are
some issues with closures inside instantiations (relating to unexported
names of closure structs).
- Renamed some typeparams in test value.go to make them all T (to make
typeparam uniqueness is working fine).
Change-Id: Ib47ed9471c19ee8e9fbb34e8506907dad3021e5a
Reviewed-on: https://go-review.googlesource.com/c/go/+/323029
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-05-17 15:00:39 -07:00
|
|
|
// node is like DeepCopy(), but substitutes ONAME nodes based on subst.ts.vars, and
|
2021-05-11 19:29:10 -07:00
|
|
|
// also descends into closures. It substitutes type arguments for type parameters
|
|
|
|
|
// in all the new nodes.
|
2021-02-03 15:45:26 -08:00
|
|
|
func (subst *subster) node(n ir.Node) ir.Node {
|
|
|
|
|
// Use closure to capture all state needed by the ir.EditChildren argument.
|
|
|
|
|
var edit func(ir.Node) ir.Node
|
|
|
|
|
edit = func(x ir.Node) ir.Node {
|
|
|
|
|
switch x.Op() {
|
2021-02-08 10:23:05 -08:00
|
|
|
case ir.OTYPE:
|
[dev.typeparams] cmd/compile: export/import of recursive generic types.
Deal with export/import of recursive generic types. This includes
typeparams which have bounds that reference the typeparam.
There are three main changes:
- Change export/import of typeparams to have an implicit "declaration"
(doDecl). We need to do a declaration of typeparams (via the
typeparam's package and unique name), because it may be referenced
within its bound during its own definition.
- We delay most of the processing of the Instantiate call until we
finish the creation of the top-most type (similar to the way we
delay CheckSize). This is because we can't do the full instantiation
properly until the base type is fully defined (with methods). The
functions delayDoInst() and resumeDoInst() delay and resume the
processing of the instantiations.
- To do the full needed type substitutions for type instantiations
during import, I had to separate out the type subster in stencil.go
and move it to subr.go in the typecheck package. The subster in
stencil.go now does node substitution and makes use of the type
subster to do type substitutions.
Notable other changes:
- In types/builtins.go, put the newly defined typeparam for a union type
(related to use of real/imag, etc.) in the current package, rather
than the builtin package, so exports/imports work properly.
- In types2, allowed NewTypeParam() to be called with a nil bound, and
allow setting the bound later. (Needed to import a typeparam whose
bound refers to the typeparam itself.)
- During import of typeparams in types2 (importer/import.go), we need
to keep an index of the typeparams by their package and unique name
(with id). Use a new map typParamIndex[] for that. Again, this is
needed to deal with typeparams whose bounds refer to the typeparam
itself.
- Added several new tests absdiffimp.go and orderedmapsimp.go. Some of
the orderemapsimp tests are commented out for now, because there are
some issues with closures inside instantiations (relating to unexported
names of closure structs).
- Renamed some typeparams in test value.go to make them all T (to make
typeparam uniqueness is working fine).
Change-Id: Ib47ed9471c19ee8e9fbb34e8506907dad3021e5a
Reviewed-on: https://go-review.googlesource.com/c/go/+/323029
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-05-17 15:00:39 -07:00
|
|
|
return ir.TypeNode(subst.ts.Typ(x.Type()))
|
2021-02-08 10:23:05 -08:00
|
|
|
|
2021-02-03 15:45:26 -08:00
|
|
|
case ir.ONAME:
|
[dev.typeparams] cmd/compile: export/import of recursive generic types.
Deal with export/import of recursive generic types. This includes
typeparams which have bounds that reference the typeparam.
There are three main changes:
- Change export/import of typeparams to have an implicit "declaration"
(doDecl). We need to do a declaration of typeparams (via the
typeparam's package and unique name), because it may be referenced
within its bound during its own definition.
- We delay most of the processing of the Instantiate call until we
finish the creation of the top-most type (similar to the way we
delay CheckSize). This is because we can't do the full instantiation
properly until the base type is fully defined (with methods). The
functions delayDoInst() and resumeDoInst() delay and resume the
processing of the instantiations.
- To do the full needed type substitutions for type instantiations
during import, I had to separate out the type subster in stencil.go
and move it to subr.go in the typecheck package. The subster in
stencil.go now does node substitution and makes use of the type
subster to do type substitutions.
Notable other changes:
- In types/builtins.go, put the newly defined typeparam for a union type
(related to use of real/imag, etc.) in the current package, rather
than the builtin package, so exports/imports work properly.
- In types2, allowed NewTypeParam() to be called with a nil bound, and
allow setting the bound later. (Needed to import a typeparam whose
bound refers to the typeparam itself.)
- During import of typeparams in types2 (importer/import.go), we need
to keep an index of the typeparams by their package and unique name
(with id). Use a new map typParamIndex[] for that. Again, this is
needed to deal with typeparams whose bounds refer to the typeparam
itself.
- Added several new tests absdiffimp.go and orderedmapsimp.go. Some of
the orderemapsimp tests are commented out for now, because there are
some issues with closures inside instantiations (relating to unexported
names of closure structs).
- Renamed some typeparams in test value.go to make them all T (to make
typeparam uniqueness is working fine).
Change-Id: Ib47ed9471c19ee8e9fbb34e8506907dad3021e5a
Reviewed-on: https://go-review.googlesource.com/c/go/+/323029
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-05-17 15:00:39 -07:00
|
|
|
if v := subst.ts.Vars[x.(*ir.Name)]; v != nil {
|
2021-02-03 15:45:26 -08:00
|
|
|
return v
|
|
|
|
|
}
|
2021-05-11 19:29:10 -07:00
|
|
|
return x
|
2021-06-01 00:56:14 +07:00
|
|
|
case ir.ONONAME:
|
|
|
|
|
// This handles the identifier in a type switch guard
|
|
|
|
|
fallthrough
|
2021-02-03 15:45:26 -08:00
|
|
|
case ir.OLITERAL, ir.ONIL:
|
|
|
|
|
if x.Sym() != nil {
|
|
|
|
|
return x
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m := ir.Copy(x)
|
|
|
|
|
if _, isExpr := m.(ir.Expr); isExpr {
|
[dev.typeparams] cmd/compile: small fixes for stenciling
- Create the stencil name using targ.Type.String(), which handles cases
where, for example, a type argument is a pointer to a named type,
etc. *obj.
- Set name.Def properly for a new stenciled func (have the symbol point
back to the associated function node). Will be required when exporting.
- Add missing copying of Func field when making copies of Name nodes.
(On purpose (it seems), Name nodes don't have a copy() function, so
we have to copy all the needed fields explicitly.)
- Deal with nil type in subster.node(), which is the type of the return
value for a function that doesn't return anything.
- Fix min to match standard want/go form, and add in float tests. Changed
Got -> got in bunch of other typeparam tests.
- Add new tests index.go, settable.go, and smallest.go (similar to
examples in the type param proposal), some of which need the above
changes.
Change-Id: I09a72302bc1fd3635a326da92405222afa222e85
Reviewed-on: https://go-review.googlesource.com/c/go/+/291109
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-10 15:26:40 -08:00
|
|
|
t := x.Type()
|
|
|
|
|
if t == nil {
|
|
|
|
|
// t can be nil only if this is a call that has no
|
|
|
|
|
// return values, so allow that and otherwise give
|
|
|
|
|
// an error.
|
2021-02-21 10:54:38 -08:00
|
|
|
_, isCallExpr := m.(*ir.CallExpr)
|
|
|
|
|
_, isStructKeyExpr := m.(*ir.StructKeyExpr)
|
[dev.typeparams] cmd/compile: fix bunch of -G=3 bugs for test cases in test/typeparams/mdempsky
1.go, 12.go: similar to calculating type sizes, we delay computing
instantiations during import until we get up to a top-level type, in
order to make sure recursive types are complete. But we should always
delay calculating sizes when we delay instantiating types, since
otherwise we may try to calculate the size of an incomplete type. So,
needed to add Defer/ResumeCheckSize in (*importReader).typ where we also
defer instantiations. (iimport.go)
2.go: when doing type substition, we have to handle named, parameterized
basic types i.e. the type has a type parameter even though the
underlying type is a basic type that doesn't depend on the parameter.
(subr.go)
3.go: for go 1.18, we allow arbitrary types in interfaces. We had
already allowed union types and tilde types, but didn't allow regular
non-interface types in Go 1.17 for compatibility. Just skip an error
in the case of 1.18. (size.go)
5.go: types2 and types1 differ in how they print out interfaces. types1
puts a space between "interface" and "{", types2 does not. So, since
some typenames come from types2 and some from types1, we need to remove
the space when printing out type arguments. (iimport.go/subr.go)
9.go: in subst.node(), we were missing the KeyExpr case where a node has
no type. The assertion is just there, to make sure we understand all the
cases where there is no type to translate. We could just remove the
whole error check. (stencil.go)
13.go: in subst.node(), missed handling the case where a method
expression is immediate called (which of course, is quite unusual, since
then there's no real reason to have used the method expression syntax in
that case). Just needed to add ir.OMETHEXPR in the OCALL switch
statement. (stencil.go)
Change-Id: I202cbe9541dfafe740e3b84b44982d6181738ea0
Reviewed-on: https://go-review.googlesource.com/c/go/+/333165
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2021-07-07 11:49:57 -07:00
|
|
|
_, isKeyExpr := m.(*ir.KeyExpr)
|
|
|
|
|
if !isCallExpr && !isStructKeyExpr && !isKeyExpr && x.Op() != ir.OPANIC &&
|
2021-03-13 22:41:51 -08:00
|
|
|
x.Op() != ir.OCLOSE {
|
[dev.typeparams] cmd/compile: small fixes for stenciling
- Create the stencil name using targ.Type.String(), which handles cases
where, for example, a type argument is a pointer to a named type,
etc. *obj.
- Set name.Def properly for a new stenciled func (have the symbol point
back to the associated function node). Will be required when exporting.
- Add missing copying of Func field when making copies of Name nodes.
(On purpose (it seems), Name nodes don't have a copy() function, so
we have to copy all the needed fields explicitly.)
- Deal with nil type in subster.node(), which is the type of the return
value for a function that doesn't return anything.
- Fix min to match standard want/go form, and add in float tests. Changed
Got -> got in bunch of other typeparam tests.
- Add new tests index.go, settable.go, and smallest.go (similar to
examples in the type param proposal), some of which need the above
changes.
Change-Id: I09a72302bc1fd3635a326da92405222afa222e85
Reviewed-on: https://go-review.googlesource.com/c/go/+/291109
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-10 15:26:40 -08:00
|
|
|
base.Fatalf(fmt.Sprintf("Nil type for %v", x))
|
|
|
|
|
}
|
2021-02-21 10:54:38 -08:00
|
|
|
} else if x.Op() != ir.OCLOSURE {
|
[dev.typeparams] cmd/compile: export/import of recursive generic types.
Deal with export/import of recursive generic types. This includes
typeparams which have bounds that reference the typeparam.
There are three main changes:
- Change export/import of typeparams to have an implicit "declaration"
(doDecl). We need to do a declaration of typeparams (via the
typeparam's package and unique name), because it may be referenced
within its bound during its own definition.
- We delay most of the processing of the Instantiate call until we
finish the creation of the top-most type (similar to the way we
delay CheckSize). This is because we can't do the full instantiation
properly until the base type is fully defined (with methods). The
functions delayDoInst() and resumeDoInst() delay and resume the
processing of the instantiations.
- To do the full needed type substitutions for type instantiations
during import, I had to separate out the type subster in stencil.go
and move it to subr.go in the typecheck package. The subster in
stencil.go now does node substitution and makes use of the type
subster to do type substitutions.
Notable other changes:
- In types/builtins.go, put the newly defined typeparam for a union type
(related to use of real/imag, etc.) in the current package, rather
than the builtin package, so exports/imports work properly.
- In types2, allowed NewTypeParam() to be called with a nil bound, and
allow setting the bound later. (Needed to import a typeparam whose
bound refers to the typeparam itself.)
- During import of typeparams in types2 (importer/import.go), we need
to keep an index of the typeparams by their package and unique name
(with id). Use a new map typParamIndex[] for that. Again, this is
needed to deal with typeparams whose bounds refer to the typeparam
itself.
- Added several new tests absdiffimp.go and orderedmapsimp.go. Some of
the orderemapsimp tests are commented out for now, because there are
some issues with closures inside instantiations (relating to unexported
names of closure structs).
- Renamed some typeparams in test value.go to make them all T (to make
typeparam uniqueness is working fine).
Change-Id: Ib47ed9471c19ee8e9fbb34e8506907dad3021e5a
Reviewed-on: https://go-review.googlesource.com/c/go/+/323029
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-05-17 15:00:39 -07:00
|
|
|
m.SetType(subst.ts.Typ(x.Type()))
|
[dev.typeparams] cmd/compile: small fixes for stenciling
- Create the stencil name using targ.Type.String(), which handles cases
where, for example, a type argument is a pointer to a named type,
etc. *obj.
- Set name.Def properly for a new stenciled func (have the symbol point
back to the associated function node). Will be required when exporting.
- Add missing copying of Func field when making copies of Name nodes.
(On purpose (it seems), Name nodes don't have a copy() function, so
we have to copy all the needed fields explicitly.)
- Deal with nil type in subster.node(), which is the type of the return
value for a function that doesn't return anything.
- Fix min to match standard want/go form, and add in float tests. Changed
Got -> got in bunch of other typeparam tests.
- Add new tests index.go, settable.go, and smallest.go (similar to
examples in the type param proposal), some of which need the above
changes.
Change-Id: I09a72302bc1fd3635a326da92405222afa222e85
Reviewed-on: https://go-review.googlesource.com/c/go/+/291109
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-02-10 15:26:40 -08:00
|
|
|
}
|
2021-02-03 15:45:26 -08:00
|
|
|
}
|
2021-06-28 18:04:58 -07:00
|
|
|
|
|
|
|
|
for i, de := range subst.info.gfInfo.subDictCalls {
|
|
|
|
|
if de == x {
|
|
|
|
|
// Remember the dictionary entry associated with this
|
|
|
|
|
// node in the instantiated function
|
|
|
|
|
// TODO: make sure this remains correct with respect to the
|
|
|
|
|
// transformations below.
|
|
|
|
|
subst.info.dictEntryMap[m] = subst.info.startSubDict + i
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-03 15:45:26 -08:00
|
|
|
ir.EditChildren(m, edit)
|
2021-02-08 14:33:51 -08:00
|
|
|
|
2021-05-14 16:35:04 -07:00
|
|
|
m.SetTypecheck(1)
|
|
|
|
|
if typecheck.IsCmp(x.Op()) {
|
|
|
|
|
transformCompare(m.(*ir.BinaryExpr))
|
|
|
|
|
} else {
|
|
|
|
|
switch x.Op() {
|
|
|
|
|
case ir.OSLICE, ir.OSLICE3:
|
|
|
|
|
transformSlice(m.(*ir.SliceExpr))
|
|
|
|
|
|
|
|
|
|
case ir.OADD:
|
|
|
|
|
m = transformAdd(m.(*ir.BinaryExpr))
|
|
|
|
|
|
|
|
|
|
case ir.OINDEX:
|
|
|
|
|
transformIndex(m.(*ir.IndexExpr))
|
|
|
|
|
|
|
|
|
|
case ir.OAS2:
|
|
|
|
|
as2 := m.(*ir.AssignListStmt)
|
|
|
|
|
transformAssign(as2, as2.Lhs, as2.Rhs)
|
|
|
|
|
|
|
|
|
|
case ir.OAS:
|
|
|
|
|
as := m.(*ir.AssignStmt)
|
|
|
|
|
if as.Y != nil {
|
|
|
|
|
// transformAssign doesn't handle the case
|
|
|
|
|
// of zeroing assignment of a dcl (rhs[0] is nil).
|
2021-03-23 10:19:11 -07:00
|
|
|
lhs, rhs := []ir.Node{as.X}, []ir.Node{as.Y}
|
|
|
|
|
transformAssign(as, lhs, rhs)
|
2021-05-14 16:35:04 -07:00
|
|
|
}
|
2021-03-23 10:19:11 -07:00
|
|
|
|
2021-05-14 16:35:04 -07:00
|
|
|
case ir.OASOP:
|
|
|
|
|
as := m.(*ir.AssignOpStmt)
|
|
|
|
|
transformCheckAssign(as, as.X)
|
2021-03-23 10:19:11 -07:00
|
|
|
|
2021-05-14 16:35:04 -07:00
|
|
|
case ir.ORETURN:
|
|
|
|
|
transformReturn(m.(*ir.ReturnStmt))
|
2021-03-29 08:28:01 -07:00
|
|
|
|
2021-05-14 16:35:04 -07:00
|
|
|
case ir.OSEND:
|
|
|
|
|
transformSend(m.(*ir.SendStmt))
|
2021-03-29 08:28:01 -07:00
|
|
|
|
cmd/compile: replace calls to typecheck with transform functions
For additions, compares, and slices, create transform functions that do
just the transformations for those nodes by the typecheck package (given
that the code has been fully typechecked by types2). For nodes that have
no args with typeparams, we call these transform functions directly in
noder2. But for nodes that have args with typeparams, we have to delay
and call the tranform functions during stenciling, since we don't know
the specific types involved.
We indicate that a node still needs transformation by setting Typecheck
to a new value 3. This value means the current type of the node has been
set (via types2), but the node may still need transformation.
Had to export typcheck.IsCmp and typecheck.Assignop from the typecheck
package.
Added new tests list2.go (required delaying compare typecheck/transform
because of != compare in checkList) and adder.go (requires delaying add
typecheck/transform, since it can do addition for numbers or strings).
There are several more transformation functions needed for expressions
(indexing, calls, etc.) and several more complicated ones needed for
statements (mainly various kinds of assignments).
Change-Id: I7d89d13a4108308ea0304a4b815ab60b40c59b0a
Reviewed-on: https://go-review.googlesource.com/c/go/+/303091
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-18 14:36:39 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-18 11:56:46 -07:00
|
|
|
switch x.Op() {
|
|
|
|
|
case ir.OLITERAL:
|
|
|
|
|
t := m.Type()
|
|
|
|
|
if t != x.Type() {
|
|
|
|
|
// types2 will give us a constant with a type T,
|
|
|
|
|
// if an untyped constant is used with another
|
|
|
|
|
// operand of type T (in a provably correct way).
|
|
|
|
|
// When we substitute in the type args during
|
|
|
|
|
// stenciling, we now know the real type of the
|
|
|
|
|
// constant. We may then need to change the
|
|
|
|
|
// BasicLit.val to be the correct type (e.g.
|
|
|
|
|
// convert an int64Val constant to a floatVal
|
|
|
|
|
// constant).
|
|
|
|
|
m.SetType(types.UntypedInt) // use any untyped type for DefaultLit to work
|
|
|
|
|
m = typecheck.DefaultLit(m, t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ir.OXDOT:
|
2021-03-24 14:50:02 -07:00
|
|
|
// A method value/call via a type param will have been
|
|
|
|
|
// left as an OXDOT. When we see this during stenciling,
|
|
|
|
|
// finish the transformation, now that we have the
|
|
|
|
|
// instantiated receiver type. We need to do this now,
|
|
|
|
|
// since the access/selection to the method for the real
|
|
|
|
|
// type is very different from the selection for the type
|
2021-06-27 01:28:38 +07:00
|
|
|
// param. m will be transformed to an OMETHVALUE node. It
|
2021-03-24 14:50:02 -07:00
|
|
|
// will be transformed to an ODOTMETH or ODOTINTER node if
|
|
|
|
|
// we find in the OCALL case below that the method value
|
|
|
|
|
// is actually called.
|
2021-06-09 19:30:16 -07:00
|
|
|
mse := m.(*ir.SelectorExpr)
|
|
|
|
|
if src := mse.X.Type(); src.IsShape() {
|
|
|
|
|
// The only dot on a shape type value are methods.
|
|
|
|
|
if mse.X.Op() == ir.OTYPE {
|
|
|
|
|
// Method expression T.M
|
|
|
|
|
// Fall back from shape type to concrete type.
|
|
|
|
|
src = subst.unshapifyTyp(src)
|
|
|
|
|
mse.X = ir.TypeNode(src)
|
|
|
|
|
} else {
|
|
|
|
|
// Implement x.M as a conversion-to-bound-interface
|
|
|
|
|
// 1) convert x to the bound interface
|
|
|
|
|
// 2) call M on that interface
|
[dev.typeparams] cmd/compile: add dictionary entries for itab conversion
This fix the case where a type param or derived type is converted to a
non-empty interface. Previously, we were converting to an empty
interface and then using DOTTYPE to convert to the correct non-empty
interface. In that case, we can get the needed itab directly from the
dictionary. This is needed for correctness from shapes, if the
destination interface is parameterized, else we will incorrectly convert
to the shape version of the interface.
Creating/writing an itab can involve generating wrappers for a bunch of
methods, which may use dictionaries. So, all the
dictionaries/instantiations are being generated on the fly and have
recursive relationships, it is simplest to finish creating/writing the
itabs at the end of the stenciling phase. So, we create a list of the
dictionaries which need to be completed by writing out their itab
entries.
The existing tests ordered.go, ifaceconv.go, and issue44688.go make use
of this optimization.
Got itab conversions for bound calls working, except for 13.go.
Also, want to get rid of the concretify, but I think we need more info
on the Bound from types2.
Change-Id: If552958a7b8a435500d6cc42c401572c367b30d1
Reviewed-on: https://go-review.googlesource.com/c/go/+/336993
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2021-07-12 19:34:15 -07:00
|
|
|
gsrc := x.(*ir.SelectorExpr).X.Type()
|
2021-07-23 17:19:51 -07:00
|
|
|
dst := gsrc.Bound()
|
|
|
|
|
if dst.HasTParam() {
|
|
|
|
|
dst = subst.ts.Typ(dst)
|
|
|
|
|
}
|
[dev.typeparams] cmd/compile: add dictionary entries for itab conversion
This fix the case where a type param or derived type is converted to a
non-empty interface. Previously, we were converting to an empty
interface and then using DOTTYPE to convert to the correct non-empty
interface. In that case, we can get the needed itab directly from the
dictionary. This is needed for correctness from shapes, if the
destination interface is parameterized, else we will incorrectly convert
to the shape version of the interface.
Creating/writing an itab can involve generating wrappers for a bunch of
methods, which may use dictionaries. So, all the
dictionaries/instantiations are being generated on the fly and have
recursive relationships, it is simplest to finish creating/writing the
itabs at the end of the stenciling phase. So, we create a list of the
dictionaries which need to be completed by writing out their itab
entries.
The existing tests ordered.go, ifaceconv.go, and issue44688.go make use
of this optimization.
Got itab conversions for bound calls working, except for 13.go.
Also, want to get rid of the concretify, but I think we need more info
on the Bound from types2.
Change-Id: If552958a7b8a435500d6cc42c401572c367b30d1
Reviewed-on: https://go-review.googlesource.com/c/go/+/336993
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2021-07-12 19:34:15 -07:00
|
|
|
mse.X = subst.convertUsingDictionary(m.Pos(), mse.X, x, dst, gsrc)
|
2021-06-09 19:30:16 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
transformDot(mse, false)
|
|
|
|
|
if mse.Op() == ir.OMETHEXPR && mse.X.Type().HasShape() {
|
|
|
|
|
mse.X = ir.TypeNodeAt(mse.X.Pos(), subst.unshapifyTyp(mse.X.Type()))
|
|
|
|
|
}
|
2021-03-24 14:50:02 -07:00
|
|
|
m.SetTypecheck(1)
|
2021-03-18 11:56:46 -07:00
|
|
|
|
|
|
|
|
case ir.OCALL:
|
2021-02-08 14:33:51 -08:00
|
|
|
call := m.(*ir.CallExpr)
|
2021-03-29 08:28:01 -07:00
|
|
|
switch call.X.Op() {
|
|
|
|
|
case ir.OTYPE:
|
2021-03-23 10:19:11 -07:00
|
|
|
// Transform the conversion, now that we know the
|
|
|
|
|
// type argument.
|
2021-06-08 15:58:16 -07:00
|
|
|
m = transformConvCall(call)
|
2021-07-21 16:23:17 -07:00
|
|
|
// CONVIFACE transformation was already done in node2
|
|
|
|
|
assert(m.Op() != ir.OCONVIFACE)
|
2021-03-29 08:28:01 -07:00
|
|
|
|
[dev.typeparams] cmd/compile: fix bunch of -G=3 bugs for test cases in test/typeparams/mdempsky
1.go, 12.go: similar to calculating type sizes, we delay computing
instantiations during import until we get up to a top-level type, in
order to make sure recursive types are complete. But we should always
delay calculating sizes when we delay instantiating types, since
otherwise we may try to calculate the size of an incomplete type. So,
needed to add Defer/ResumeCheckSize in (*importReader).typ where we also
defer instantiations. (iimport.go)
2.go: when doing type substition, we have to handle named, parameterized
basic types i.e. the type has a type parameter even though the
underlying type is a basic type that doesn't depend on the parameter.
(subr.go)
3.go: for go 1.18, we allow arbitrary types in interfaces. We had
already allowed union types and tilde types, but didn't allow regular
non-interface types in Go 1.17 for compatibility. Just skip an error
in the case of 1.18. (size.go)
5.go: types2 and types1 differ in how they print out interfaces. types1
puts a space between "interface" and "{", types2 does not. So, since
some typenames come from types2 and some from types1, we need to remove
the space when printing out type arguments. (iimport.go/subr.go)
9.go: in subst.node(), we were missing the KeyExpr case where a node has
no type. The assertion is just there, to make sure we understand all the
cases where there is no type to translate. We could just remove the
whole error check. (stencil.go)
13.go: in subst.node(), missed handling the case where a method
expression is immediate called (which of course, is quite unusual, since
then there's no real reason to have used the method expression syntax in
that case). Just needed to add ir.OMETHEXPR in the OCALL switch
statement. (stencil.go)
Change-Id: I202cbe9541dfafe740e3b84b44982d6181738ea0
Reviewed-on: https://go-review.googlesource.com/c/go/+/333165
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2021-07-07 11:49:57 -07:00
|
|
|
case ir.OMETHVALUE, ir.OMETHEXPR:
|
2021-03-24 14:50:02 -07:00
|
|
|
// Redo the transformation of OXDOT, now that we
|
2021-03-23 10:19:11 -07:00
|
|
|
// know the method value is being called. Then
|
|
|
|
|
// transform the call.
|
2021-02-09 15:13:19 -08:00
|
|
|
call.X.(*ir.SelectorExpr).SetOp(ir.OXDOT)
|
2021-03-24 14:50:02 -07:00
|
|
|
transformDot(call.X.(*ir.SelectorExpr), true)
|
2021-06-09 19:30:16 -07:00
|
|
|
call.X.SetType(subst.unshapifyTyp(call.X.Type()))
|
2021-03-23 10:19:11 -07:00
|
|
|
transformCall(call)
|
2021-03-29 08:28:01 -07:00
|
|
|
|
|
|
|
|
case ir.ODOT, ir.ODOTPTR:
|
2021-03-14 13:46:23 -07:00
|
|
|
// An OXDOT for a generic receiver was resolved to
|
|
|
|
|
// an access to a field which has a function
|
2021-03-23 10:19:11 -07:00
|
|
|
// value. Transform the call to that function, now
|
2021-03-14 13:46:23 -07:00
|
|
|
// that the OXDOT was resolved.
|
2021-03-23 10:19:11 -07:00
|
|
|
transformCall(call)
|
2021-03-29 08:28:01 -07:00
|
|
|
|
|
|
|
|
case ir.ONAME:
|
|
|
|
|
name := call.X.Name()
|
|
|
|
|
if name.BuiltinOp != ir.OXXX {
|
|
|
|
|
switch name.BuiltinOp {
|
2021-07-20 09:37:35 -07:00
|
|
|
case ir.OMAKE, ir.OREAL, ir.OIMAG, ir.OAPPEND, ir.ODELETE:
|
2021-03-29 08:28:01 -07:00
|
|
|
// Transform these builtins now that we
|
|
|
|
|
// know the type of the args.
|
|
|
|
|
m = transformBuiltin(call)
|
|
|
|
|
default:
|
|
|
|
|
base.FatalfAt(call.Pos(), "Unexpected builtin op")
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// This is the case of a function value that was a
|
|
|
|
|
// type parameter (implied to be a function via a
|
|
|
|
|
// structural constraint) which is now resolved.
|
|
|
|
|
transformCall(call)
|
cmd/compile: getting more built-ins to work with generics
For Builtin ops, we currently stay with using the old
typechecker to transform the call to a more specific expression
and possibly use more specific ops. However, for a bunch of the
ops, we delay calling the old typechecker if any of the args have
type params, for a variety of reasons.
In the near future, we will start creating separate functions that do
the same transformations as the old typechecker for calls, builtins,
indexing, comparisons, etc. These functions can then be called at noder
time for nodes with no type params, and at stenciling time for nodes
with type params.
Remove unnecessary calls to types1 typechecker for most kinds of
statements (still need it for SendStmt, AssignStmt, ReturnStmt, and
SelectStmt). In particular, we don't need it for RangeStmt, and this
avoids some complaints by the types1 typechecker on generic code.
Other small changes:
- Fix check on whether to delay calling types1-typechecker on type
conversions. Should check if HasTParam is true, rather than if the
type is directly a TYPEPARAM.
- Don't call types1-typechecker on an indexing operation if the left
operand has a typeparam in its type and is not obviously a TMAP,
TSLICE, or TARRAY. As above, we will eventually have to create a new
function that can do the required transformations (for complicated
cases) at noder time or stenciling time.
- Copy n.BuiltinOp in subster.node()
- The complex arithmetic example in absdiff.go now works.
- Added new tests double.go and append.go
- Added new example with a new() call in settable.go
Change-Id: I8f377afb6126cab1826bd3c2732aa8cdf1f7e0b4
Reviewed-on: https://go-review.googlesource.com/c/go/+/301951
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-12 11:36:02 -08:00
|
|
|
}
|
|
|
|
|
|
2021-03-29 08:28:01 -07:00
|
|
|
case ir.OCLOSURE:
|
|
|
|
|
transformCall(call)
|
|
|
|
|
|
|
|
|
|
case ir.OFUNCINST:
|
|
|
|
|
// A call with an OFUNCINST will get transformed
|
2021-03-13 22:41:51 -08:00
|
|
|
// in stencil() once we have created & attached the
|
|
|
|
|
// instantiation to be called.
|
2021-03-29 08:28:01 -07:00
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
base.FatalfAt(call.Pos(), fmt.Sprintf("Unexpected op with CALL during stenciling: %v", call.X.Op()))
|
2021-02-08 14:33:51 -08:00
|
|
|
}
|
|
|
|
|
|
2021-03-18 11:56:46 -07:00
|
|
|
case ir.OCLOSURE:
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
// We're going to create a new closure from scratch, so clear m
|
|
|
|
|
// to avoid using the ir.Copy by accident until we reassign it.
|
|
|
|
|
m = nil
|
|
|
|
|
|
2021-02-03 15:45:26 -08:00
|
|
|
x := x.(*ir.ClosureExpr)
|
2021-04-09 06:30:20 -07:00
|
|
|
// Need to duplicate x.Func.Nname, x.Func.Dcl, x.Func.ClosureVars, and
|
2021-02-03 15:45:26 -08:00
|
|
|
// x.Func.Body.
|
|
|
|
|
oldfn := x.Func
|
2021-06-12 07:33:18 -07:00
|
|
|
newfn := ir.NewClosureFunc(oldfn.Pos(), subst.newf != nil)
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
ir.NameClosure(newfn.OClosure, subst.newf)
|
|
|
|
|
|
2021-02-21 10:54:38 -08:00
|
|
|
saveNewf := subst.newf
|
2021-03-29 08:28:01 -07:00
|
|
|
ir.CurFunc = newfn
|
2021-02-21 10:54:38 -08:00
|
|
|
subst.newf = newfn
|
|
|
|
|
newfn.Dcl = subst.namelist(oldfn.Dcl)
|
2021-06-28 18:04:58 -07:00
|
|
|
|
|
|
|
|
// Make a closure variable for the dictionary of the
|
|
|
|
|
// containing function.
|
|
|
|
|
cdict := ir.CaptureName(oldfn.Pos(), newfn, subst.info.dictParam)
|
|
|
|
|
typed(types.Types[types.TUINTPTR], cdict)
|
|
|
|
|
ir.FinishCaptureNames(oldfn.Pos(), saveNewf, newfn)
|
|
|
|
|
newfn.ClosureVars = append(newfn.ClosureVars, subst.namelist(oldfn.ClosureVars)...)
|
|
|
|
|
|
|
|
|
|
// Create inst info for the instantiated closure. The dict
|
|
|
|
|
// param is the closure variable for the dictionary of the
|
|
|
|
|
// outer function. Since the dictionary is shared, use the
|
|
|
|
|
// same entries for startSubDict, dictLen, dictEntryMap.
|
|
|
|
|
cinfo := &instInfo{
|
|
|
|
|
fun: newfn,
|
|
|
|
|
dictParam: cdict,
|
|
|
|
|
startSubDict: subst.info.startSubDict,
|
|
|
|
|
dictLen: subst.info.dictLen,
|
|
|
|
|
dictEntryMap: subst.info.dictEntryMap,
|
|
|
|
|
}
|
|
|
|
|
subst.g.instInfoMap[newfn.Nname.Sym()] = cinfo
|
2021-02-21 10:54:38 -08:00
|
|
|
|
[dev.typeparams] cmd/compile: export/import of recursive generic types.
Deal with export/import of recursive generic types. This includes
typeparams which have bounds that reference the typeparam.
There are three main changes:
- Change export/import of typeparams to have an implicit "declaration"
(doDecl). We need to do a declaration of typeparams (via the
typeparam's package and unique name), because it may be referenced
within its bound during its own definition.
- We delay most of the processing of the Instantiate call until we
finish the creation of the top-most type (similar to the way we
delay CheckSize). This is because we can't do the full instantiation
properly until the base type is fully defined (with methods). The
functions delayDoInst() and resumeDoInst() delay and resume the
processing of the instantiations.
- To do the full needed type substitutions for type instantiations
during import, I had to separate out the type subster in stencil.go
and move it to subr.go in the typecheck package. The subster in
stencil.go now does node substitution and makes use of the type
subster to do type substitutions.
Notable other changes:
- In types/builtins.go, put the newly defined typeparam for a union type
(related to use of real/imag, etc.) in the current package, rather
than the builtin package, so exports/imports work properly.
- In types2, allowed NewTypeParam() to be called with a nil bound, and
allow setting the bound later. (Needed to import a typeparam whose
bound refers to the typeparam itself.)
- During import of typeparams in types2 (importer/import.go), we need
to keep an index of the typeparams by their package and unique name
(with id). Use a new map typParamIndex[] for that. Again, this is
needed to deal with typeparams whose bounds refer to the typeparam
itself.
- Added several new tests absdiffimp.go and orderedmapsimp.go. Some of
the orderemapsimp tests are commented out for now, because there are
some issues with closures inside instantiations (relating to unexported
names of closure structs).
- Renamed some typeparams in test value.go to make them all T (to make
typeparam uniqueness is working fine).
Change-Id: Ib47ed9471c19ee8e9fbb34e8506907dad3021e5a
Reviewed-on: https://go-review.googlesource.com/c/go/+/323029
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-05-17 15:00:39 -07:00
|
|
|
typed(subst.ts.Typ(oldfn.Nname.Type()), newfn.Nname)
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
typed(newfn.Nname.Type(), newfn.OClosure)
|
2021-02-21 10:54:38 -08:00
|
|
|
newfn.SetTypecheck(1)
|
2021-03-29 08:28:01 -07:00
|
|
|
|
|
|
|
|
// Make sure type of closure function is set before doing body.
|
|
|
|
|
newfn.Body = subst.list(oldfn.Body)
|
|
|
|
|
subst.newf = saveNewf
|
|
|
|
|
ir.CurFunc = saveNewf
|
|
|
|
|
|
[dev.typeparams] cmd/compile: refactor closure construction
typecheck.tcClosure is complicated with many code flows because all of
its callers setup the closure funcs in slightly different ways. E.g.,
it's non-obvious who's responsible for setting the underlying func's
Sym or adding it to target.Decls, or how to write new code that
constructs a closure without interfering with existing code.
This CL refactors everything to use three common functions in package
ir: NewClosureFunc (which handle creating the Func, Name, and
ClosureExpr and wiring them together), NameClosure (which generates
and assigns its unique Sym), and UseClosure (which handles adding the
Func to target.Decls).
Most IR builders can actually name the closure right away, but the
legacy noder+typecheck path may not yet know the name of the enclosing
function. In particular, for methods declared with aliased receiver
parameters, we need to wait until after typechecking top-level
declarations to know the method's true name. So they're left anonymous
until typecheck.
UseClosure does relatively little work today, but it serves as a
useful spot to check that the code setting up closures got it right.
It may also eventually serve as an optimization point for early
lifting of trivial closures, which may or may not ultimately be
beneficial.
Change-Id: I7da1e93c70d268f575b12d6aaeb2336eb910a6f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/327051
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
2021-06-11 03:09:26 -07:00
|
|
|
m = ir.UseClosure(newfn.OClosure, subst.g.target)
|
|
|
|
|
m.(*ir.ClosureExpr).SetInit(subst.list(x.Init()))
|
2021-06-04 17:19:09 -07:00
|
|
|
|
|
|
|
|
case ir.OCONVIFACE:
|
|
|
|
|
x := x.(*ir.ConvExpr)
|
2021-06-08 15:58:16 -07:00
|
|
|
// Note: x's argument is still typed as a type parameter.
|
|
|
|
|
// m's argument now has an instantiated type.
|
[dev.typeparams] cmd/compile: add dictionary entries for itab conversion
This fix the case where a type param or derived type is converted to a
non-empty interface. Previously, we were converting to an empty
interface and then using DOTTYPE to convert to the correct non-empty
interface. In that case, we can get the needed itab directly from the
dictionary. This is needed for correctness from shapes, if the
destination interface is parameterized, else we will incorrectly convert
to the shape version of the interface.
Creating/writing an itab can involve generating wrappers for a bunch of
methods, which may use dictionaries. So, all the
dictionaries/instantiations are being generated on the fly and have
recursive relationships, it is simplest to finish creating/writing the
itabs at the end of the stenciling phase. So, we create a list of the
dictionaries which need to be completed by writing out their itab
entries.
The existing tests ordered.go, ifaceconv.go, and issue44688.go make use
of this optimization.
Got itab conversions for bound calls working, except for 13.go.
Also, want to get rid of the concretify, but I think we need more info
on the Bound from types2.
Change-Id: If552958a7b8a435500d6cc42c401572c367b30d1
Reviewed-on: https://go-review.googlesource.com/c/go/+/336993
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2021-07-12 19:34:15 -07:00
|
|
|
if x.X.Type().HasTParam() {
|
|
|
|
|
m = subst.convertUsingDictionary(m.Pos(), m.(*ir.ConvExpr).X, x, m.Type(), x.X.Type())
|
2021-06-04 17:19:09 -07:00
|
|
|
}
|
2021-07-23 15:23:57 -07:00
|
|
|
case ir.ODOTTYPE, ir.ODOTTYPE2:
|
|
|
|
|
m.SetType(subst.unshapifyTyp(m.Type()))
|
2021-06-09 19:30:16 -07:00
|
|
|
|
|
|
|
|
case ir.OMETHEXPR:
|
|
|
|
|
se := m.(*ir.SelectorExpr)
|
|
|
|
|
se.X = ir.TypeNodeAt(se.X.Pos(), subst.unshapifyTyp(se.X.Type()))
|
|
|
|
|
case ir.OFUNCINST:
|
|
|
|
|
inst := m.(*ir.InstExpr)
|
|
|
|
|
targs2 := make([]ir.Node, len(inst.Targs))
|
|
|
|
|
for i, n := range inst.Targs {
|
|
|
|
|
targs2[i] = ir.TypeNodeAt(n.Pos(), subst.unshapifyTyp(n.Type()))
|
|
|
|
|
// TODO: need an ir.Name node?
|
|
|
|
|
}
|
|
|
|
|
inst.Targs = targs2
|
2021-02-03 15:45:26 -08:00
|
|
|
}
|
|
|
|
|
return m
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return edit(n)
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-06 09:38:58 -07:00
|
|
|
// findDictType looks for type t in the typeparams or derived types in the generic
|
|
|
|
|
// function info subst.info.gfInfo. This will indicate the dictionary entry with the
|
|
|
|
|
// correct concrete type for the associated instantiated function.
|
|
|
|
|
func (subst *subster) findDictType(t *types.Type) int {
|
|
|
|
|
for i, dt := range subst.info.gfInfo.tparams {
|
|
|
|
|
if dt == t {
|
|
|
|
|
return i
|
|
|
|
|
}
|
2021-06-08 15:58:16 -07:00
|
|
|
}
|
2021-07-06 09:38:58 -07:00
|
|
|
for i, dt := range subst.info.gfInfo.derivedTypes {
|
|
|
|
|
if types.Identical(dt, t) {
|
|
|
|
|
return i + len(subst.info.gfInfo.tparams)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
[dev.typeparams] cmd/compile: add dictionary entries for itab conversion
This fix the case where a type param or derived type is converted to a
non-empty interface. Previously, we were converting to an empty
interface and then using DOTTYPE to convert to the correct non-empty
interface. In that case, we can get the needed itab directly from the
dictionary. This is needed for correctness from shapes, if the
destination interface is parameterized, else we will incorrectly convert
to the shape version of the interface.
Creating/writing an itab can involve generating wrappers for a bunch of
methods, which may use dictionaries. So, all the
dictionaries/instantiations are being generated on the fly and have
recursive relationships, it is simplest to finish creating/writing the
itabs at the end of the stenciling phase. So, we create a list of the
dictionaries which need to be completed by writing out their itab
entries.
The existing tests ordered.go, ifaceconv.go, and issue44688.go make use
of this optimization.
Got itab conversions for bound calls working, except for 13.go.
Also, want to get rid of the concretify, but I think we need more info
on the Bound from types2.
Change-Id: If552958a7b8a435500d6cc42c401572c367b30d1
Reviewed-on: https://go-review.googlesource.com/c/go/+/336993
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2021-07-12 19:34:15 -07:00
|
|
|
// convertUsingDictionary converts value v from instantiated type src to an interface
|
|
|
|
|
// type dst, by returning a new set of nodes that make use of a dictionary entry. src
|
|
|
|
|
// is the generic (not shape) type, and gn is the original generic node of the
|
|
|
|
|
// CONVIFACE node or XDOT node (for a bound method call) that is causing the
|
|
|
|
|
// conversion.
|
|
|
|
|
func (subst *subster) convertUsingDictionary(pos src.XPos, v ir.Node, gn ir.Node, dst, src *types.Type) ir.Node {
|
|
|
|
|
assert(src.HasTParam())
|
|
|
|
|
assert(dst.IsInterface())
|
|
|
|
|
|
|
|
|
|
var rt ir.Node
|
|
|
|
|
if !dst.IsEmptyInterface() {
|
|
|
|
|
// We should have an itab entry in the dictionary. Using this itab
|
|
|
|
|
// will be more efficient than converting to an empty interface first
|
|
|
|
|
// and then type asserting to dst.
|
|
|
|
|
ix := -1
|
|
|
|
|
for i, ic := range subst.info.gfInfo.itabConvs {
|
|
|
|
|
if ic == gn {
|
|
|
|
|
ix = subst.info.startItabConv + i
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
assert(ix >= 0)
|
|
|
|
|
rt = getDictionaryEntry(pos, subst.info.dictParam, ix, subst.info.dictLen)
|
|
|
|
|
} else {
|
|
|
|
|
ix := subst.findDictType(src)
|
|
|
|
|
assert(ix >= 0)
|
|
|
|
|
// Load the actual runtime._type of the type parameter from the dictionary.
|
|
|
|
|
rt = subst.getDictionaryType(pos, ix)
|
2021-06-08 15:58:16 -07:00
|
|
|
}
|
|
|
|
|
|
2021-07-23 18:10:58 -07:00
|
|
|
// Figure out what the data field of the interface will be.
|
|
|
|
|
var data ir.Node
|
|
|
|
|
if v.Type().IsInterface() {
|
|
|
|
|
data = ir.NewUnaryExpr(pos, ir.OIDATA, v)
|
|
|
|
|
} else {
|
|
|
|
|
data = ir.NewConvExpr(pos, ir.OCONVIDATA, nil, v)
|
2021-06-08 15:58:16 -07:00
|
|
|
}
|
|
|
|
|
typed(types.Types[types.TUNSAFEPTR], data)
|
2021-07-23 18:10:58 -07:00
|
|
|
|
|
|
|
|
// Build an interface from the type and data parts.
|
2021-06-08 15:58:16 -07:00
|
|
|
var i ir.Node = ir.NewBinaryExpr(pos, ir.OEFACE, rt, data)
|
|
|
|
|
typed(dst, i)
|
|
|
|
|
return i
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-21 10:54:38 -08:00
|
|
|
func (subst *subster) namelist(l []*ir.Name) []*ir.Name {
|
|
|
|
|
s := make([]*ir.Name, len(l))
|
|
|
|
|
for i, n := range l {
|
2021-05-11 19:29:10 -07:00
|
|
|
s[i] = subst.localvar(n)
|
2021-02-21 10:54:38 -08:00
|
|
|
if n.Defn != nil {
|
|
|
|
|
s[i].Defn = subst.node(n.Defn)
|
|
|
|
|
}
|
|
|
|
|
if n.Outer != nil {
|
|
|
|
|
s[i].Outer = subst.node(n.Outer).(*ir.Name)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-03 15:45:26 -08:00
|
|
|
func (subst *subster) list(l []ir.Node) []ir.Node {
|
|
|
|
|
s := make([]ir.Node, len(l))
|
|
|
|
|
for i, n := range l {
|
|
|
|
|
s[i] = subst.node(n)
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// fields sets the Nname field for the Field nodes inside a type signature, based
|
|
|
|
|
// on the corresponding in/out parameters in dcl. It depends on the in and out
|
|
|
|
|
// parameters being in order in dcl.
|
2021-02-11 10:50:20 -08:00
|
|
|
func (subst *subster) fields(class ir.Class, oldfields []*types.Field, dcl []*ir.Name) []*types.Field {
|
2021-02-03 15:45:26 -08:00
|
|
|
// Find the starting index in dcl of declarations of the class (either
|
|
|
|
|
// PPARAM or PPARAMOUT).
|
2021-03-30 13:42:14 -07:00
|
|
|
var i int
|
2021-02-03 15:45:26 -08:00
|
|
|
for i = range dcl {
|
|
|
|
|
if dcl[i].Class == class {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create newfields nodes that are copies of the oldfields nodes, but
|
|
|
|
|
// with substitution for any type params, and with Nname set to be the node in
|
|
|
|
|
// Dcl for the corresponding PPARAM or PPARAMOUT.
|
2021-03-30 13:42:14 -07:00
|
|
|
newfields := make([]*types.Field, len(oldfields))
|
2021-02-03 15:45:26 -08:00
|
|
|
for j := range oldfields {
|
|
|
|
|
newfields[j] = oldfields[j].Copy()
|
[dev.typeparams] cmd/compile: export/import of recursive generic types.
Deal with export/import of recursive generic types. This includes
typeparams which have bounds that reference the typeparam.
There are three main changes:
- Change export/import of typeparams to have an implicit "declaration"
(doDecl). We need to do a declaration of typeparams (via the
typeparam's package and unique name), because it may be referenced
within its bound during its own definition.
- We delay most of the processing of the Instantiate call until we
finish the creation of the top-most type (similar to the way we
delay CheckSize). This is because we can't do the full instantiation
properly until the base type is fully defined (with methods). The
functions delayDoInst() and resumeDoInst() delay and resume the
processing of the instantiations.
- To do the full needed type substitutions for type instantiations
during import, I had to separate out the type subster in stencil.go
and move it to subr.go in the typecheck package. The subster in
stencil.go now does node substitution and makes use of the type
subster to do type substitutions.
Notable other changes:
- In types/builtins.go, put the newly defined typeparam for a union type
(related to use of real/imag, etc.) in the current package, rather
than the builtin package, so exports/imports work properly.
- In types2, allowed NewTypeParam() to be called with a nil bound, and
allow setting the bound later. (Needed to import a typeparam whose
bound refers to the typeparam itself.)
- During import of typeparams in types2 (importer/import.go), we need
to keep an index of the typeparams by their package and unique name
(with id). Use a new map typParamIndex[] for that. Again, this is
needed to deal with typeparams whose bounds refer to the typeparam
itself.
- Added several new tests absdiffimp.go and orderedmapsimp.go. Some of
the orderemapsimp tests are commented out for now, because there are
some issues with closures inside instantiations (relating to unexported
names of closure structs).
- Renamed some typeparams in test value.go to make them all T (to make
typeparam uniqueness is working fine).
Change-Id: Ib47ed9471c19ee8e9fbb34e8506907dad3021e5a
Reviewed-on: https://go-review.googlesource.com/c/go/+/323029
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-05-17 15:00:39 -07:00
|
|
|
newfields[j].Type = subst.ts.Typ(oldfields[j].Type)
|
[dev.typeparams] cmd/compile: get export/import of generic types & functions working
The general idea is that we now export/import typeparams, typeparam
lists for generic types and functions, and instantiated types
(instantiations of generic types with either new typeparams or concrete
types).
This changes the export format -- the next CL in the stack adds the
export versions and checks for it in the appropriate places.
We always export/import generic function bodies, using the same code
that we use for exporting/importing the bodies of inlineable functions.
To avoid complicated scoping, we consider all type params as unique and
give them unique names for types1. We therefore include the types2 ids
(subscripts) in the export format and re-create on import. We always
access the same unique types1 typeParam type for the same typeparam
name.
We create fully-instantiated generic types and functions in the original
source package. We do an extra NeedRuntimeType() call to make sure that
the correct DWARF information is written out. We call SetDupOK(true) for
the functions/methods to have the linker automatically drop duplicate
instantiations.
Other miscellaneous details:
- Export/import of typeparam bounds works for methods (but not
typelists) for now, but will change with the typeset changes.
- Added a new types.Instantiate function roughly analogous to the
types2.Instantiate function recently added.
- Always access methods info from the original/base generic type, since
the methods of an instantiated type are not filled in (in types2 or
types1).
- New field OrigSym in types.Type to keep track of base generic type
that instantiated type was based on. We use the generic type's symbol
(OrigSym) as the link, rather than a Type pointer, since we haven't
always created the base type yet when we want to set the link (during
types2 to types1 conversion).
- Added types2.AsTypeParam(), (*types2.TypeParam).SetId()
- New test minimp.dir, which tests use of generic function Min across
packages. Another test stringimp.dir, which also exports a generic
function Stringify across packages, where the type param has a bound
(Stringer) as well. New test pairimp.dir, which tests use of generic
type Pair (with no methods) across packages.
- New test valimp.dir, which tests use of generic type (with methods
and related functions) across packages.
- Modified several other tests (adder.go, settable.go, smallest.go,
stringable.go, struct.go, sum.go) to export their generic
functions/types to show that generic functions/types can be exported
successfully (but this doesn't test import).
Change-Id: Ie61ce9d54a46d368ddc7a76c41399378963bb57f
Reviewed-on: https://go-review.googlesource.com/c/go/+/319930
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-04-13 15:37:36 -07:00
|
|
|
// A PPARAM field will be missing from dcl if its name is
|
2021-03-30 13:42:14 -07:00
|
|
|
// unspecified or specified as "_". So, we compare the dcl sym
|
[dev.typeparams] cmd/compile: get export/import of generic types & functions working
The general idea is that we now export/import typeparams, typeparam
lists for generic types and functions, and instantiated types
(instantiations of generic types with either new typeparams or concrete
types).
This changes the export format -- the next CL in the stack adds the
export versions and checks for it in the appropriate places.
We always export/import generic function bodies, using the same code
that we use for exporting/importing the bodies of inlineable functions.
To avoid complicated scoping, we consider all type params as unique and
give them unique names for types1. We therefore include the types2 ids
(subscripts) in the export format and re-create on import. We always
access the same unique types1 typeParam type for the same typeparam
name.
We create fully-instantiated generic types and functions in the original
source package. We do an extra NeedRuntimeType() call to make sure that
the correct DWARF information is written out. We call SetDupOK(true) for
the functions/methods to have the linker automatically drop duplicate
instantiations.
Other miscellaneous details:
- Export/import of typeparam bounds works for methods (but not
typelists) for now, but will change with the typeset changes.
- Added a new types.Instantiate function roughly analogous to the
types2.Instantiate function recently added.
- Always access methods info from the original/base generic type, since
the methods of an instantiated type are not filled in (in types2 or
types1).
- New field OrigSym in types.Type to keep track of base generic type
that instantiated type was based on. We use the generic type's symbol
(OrigSym) as the link, rather than a Type pointer, since we haven't
always created the base type yet when we want to set the link (during
types2 to types1 conversion).
- Added types2.AsTypeParam(), (*types2.TypeParam).SetId()
- New test minimp.dir, which tests use of generic function Min across
packages. Another test stringimp.dir, which also exports a generic
function Stringify across packages, where the type param has a bound
(Stringer) as well. New test pairimp.dir, which tests use of generic
type Pair (with no methods) across packages.
- New test valimp.dir, which tests use of generic type (with methods
and related functions) across packages.
- Modified several other tests (adder.go, settable.go, smallest.go,
stringable.go, struct.go, sum.go) to export their generic
functions/types to show that generic functions/types can be exported
successfully (but this doesn't test import).
Change-Id: Ie61ce9d54a46d368ddc7a76c41399378963bb57f
Reviewed-on: https://go-review.googlesource.com/c/go/+/319930
Trust: Dan Scales <danscales@google.com>
Trust: Robert Griesemer <gri@golang.org>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-04-13 15:37:36 -07:00
|
|
|
// with the field sym (or sym of the field's Nname node). (Unnamed
|
|
|
|
|
// results still have a name like ~r2 in their Nname node.) If
|
|
|
|
|
// they don't match, this dcl (if there is one left) must apply to
|
|
|
|
|
// a later field.
|
|
|
|
|
if i < len(dcl) && (dcl[i].Sym() == oldfields[j].Sym ||
|
|
|
|
|
(oldfields[j].Nname != nil && dcl[i].Sym() == oldfields[j].Nname.Sym())) {
|
2021-03-30 13:42:14 -07:00
|
|
|
newfields[j].Nname = dcl[i]
|
|
|
|
|
i++
|
|
|
|
|
}
|
2021-02-03 15:45:26 -08:00
|
|
|
}
|
|
|
|
|
return newfields
|
|
|
|
|
}
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
|
2021-07-06 10:53:00 -07:00
|
|
|
// deref does a single deref of type t, if it is a pointer type.
|
cmd/compile: get instantiated generic types working with interfaces
Get instantiatiated generic types working with interfaces, including
typechecking assignments to interfaces and instantiating all the methods
properly. To get it all working, this change includes:
- Add support for substituting in interfaces in subster.typ()
- Fill in the info for the methods for all instantiated generic types,
so those methods will be available for later typechecking (by the old
typechecker) when assigning an instantiated generic type to an
interface. We also want those methods available so we have the list
when we want to instantiate all methods of an instantiated type. We
have both for instantiated types encountered during the initial noder
phase, and for instantiated types created during stenciling of a
function/method.
- When we first create a fully-instantiated generic type (whether
during initial noder2 pass or while instantiating a method/function),
add it to a list so that all of its methods will also be
instantiated. This is needed so that an instantiated type can be
assigned to an interface.
- Properly substitute type names in the names of instantiated methods.
- New accessor methods for types.Type.RParam.
- To deal with generic types which are empty structs (or just don't use
their type params anywhere), we want to set HasTParam if a named type
has any type params that are not fully instantiated, even if the
type param is not used in the type.
- In subst.typ() and elsewhere, always set sym.Def for a new forwarding
type we are creating, so we always create a single unique type for
each generic type instantiation. This handles recursion within a
type, and also recursive relationships across many types or methods.
We remove the seen[] hashtable, which was serving the same purpose,
but for subst.typ() only. We now handle all kinds of recursive types.
- We don't seem to need to force types.CheckSize() on
created/substituted generic types anymore, so commented out for now.
- Add an RParams accessor to types2.Signature, and also a new
exported types2.AsSignature() function.
Change-Id: If6c5dd98427b20bfe9de3379cc16f83df9c9b632
Reviewed-on: https://go-review.googlesource.com/c/go/+/298449
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Dan Scales <danscales@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
2021-03-03 13:33:27 -08:00
|
|
|
func deref(t *types.Type) *types.Type {
|
|
|
|
|
if t.IsPtr() {
|
|
|
|
|
return t.Elem()
|
|
|
|
|
}
|
|
|
|
|
return t
|
|
|
|
|
}
|
2021-06-07 18:13:15 -07:00
|
|
|
|
2021-07-25 18:27:15 -07:00
|
|
|
// markTypeUsed marks type t as used in order to help avoid dead-code elimination of
|
|
|
|
|
// needed methods.
|
|
|
|
|
func markTypeUsed(t *types.Type, lsym *obj.LSym) {
|
|
|
|
|
if t.IsInterface() {
|
|
|
|
|
// Mark all the methods of the interface as used.
|
|
|
|
|
// TODO: we should really only mark the interface methods
|
|
|
|
|
// that are actually called in the application.
|
|
|
|
|
for i, _ := range t.AllMethods().Slice() {
|
|
|
|
|
reflectdata.MarkUsedIfaceMethodIndex(lsym, t, i)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// TODO: This is somewhat overkill, we really only need it
|
|
|
|
|
// for types that are put into interfaces.
|
|
|
|
|
reflectdata.MarkTypeUsedInInterface(t, lsym)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-26 18:18:16 -07:00
|
|
|
// getDictionarySym returns the dictionary for the named generic function gf, which
|
|
|
|
|
// is instantiated with the type arguments targs.
|
|
|
|
|
func (g *irgen) getDictionarySym(gf *ir.Name, targs []*types.Type, isMeth bool) *types.Sym {
|
2021-06-07 18:13:15 -07:00
|
|
|
if len(targs) == 0 {
|
2021-06-26 18:18:16 -07:00
|
|
|
base.Fatalf("%s should have type arguments", gf.Sym().Name)
|
2021-06-07 18:13:15 -07:00
|
|
|
}
|
|
|
|
|
|
2021-06-09 19:30:16 -07:00
|
|
|
// Enforce that only concrete types can make it to here.
|
|
|
|
|
for _, t := range targs {
|
2021-07-23 15:23:57 -07:00
|
|
|
if t.HasShape() {
|
2021-06-09 19:30:16 -07:00
|
|
|
panic(fmt.Sprintf("shape %+v in dictionary for %s", t, gf.Sym().Name))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-07 18:13:15 -07:00
|
|
|
// Get a symbol representing the dictionary.
|
2021-06-26 18:18:16 -07:00
|
|
|
sym := typecheck.MakeDictName(gf.Sym(), targs, isMeth)
|
2021-06-07 18:13:15 -07:00
|
|
|
|
|
|
|
|
// Initialize the dictionary, if we haven't yet already.
|
|
|
|
|
if lsym := sym.Linksym(); len(lsym.P) == 0 {
|
2021-07-02 12:32:38 -07:00
|
|
|
info := g.getGfInfo(gf)
|
|
|
|
|
|
2021-06-28 18:04:58 -07:00
|
|
|
infoPrint("=== Creating dictionary %v\n", sym.Name)
|
2021-06-07 18:13:15 -07:00
|
|
|
off := 0
|
|
|
|
|
// Emit an entry for each targ (concrete type or gcshape).
|
|
|
|
|
for _, t := range targs {
|
|
|
|
|
infoPrint(" * %v\n", t)
|
|
|
|
|
s := reflectdata.TypeLinksym(t)
|
|
|
|
|
off = objw.SymPtr(lsym, off, s, 0)
|
2021-07-25 18:27:15 -07:00
|
|
|
markTypeUsed(t, lsym)
|
2021-06-07 18:13:15 -07:00
|
|
|
}
|
|
|
|
|
subst := typecheck.Tsubster{
|
|
|
|
|
Tparams: info.tparams,
|
|
|
|
|
Targs: targs,
|
|
|
|
|
}
|
|
|
|
|
// Emit an entry for each derived type (after substituting targs)
|
|
|
|
|
for _, t := range info.derivedTypes {
|
|
|
|
|
ts := subst.Typ(t)
|
|
|
|
|
infoPrint(" - %v\n", ts)
|
|
|
|
|
s := reflectdata.TypeLinksym(ts)
|
|
|
|
|
off = objw.SymPtr(lsym, off, s, 0)
|
2021-07-25 18:27:15 -07:00
|
|
|
markTypeUsed(ts, lsym)
|
2021-06-07 18:13:15 -07:00
|
|
|
}
|
|
|
|
|
// Emit an entry for each subdictionary (after substituting targs)
|
|
|
|
|
for _, n := range info.subDictCalls {
|
2021-06-26 18:18:16 -07:00
|
|
|
var sym *types.Sym
|
2021-06-28 18:04:58 -07:00
|
|
|
switch n.Op() {
|
|
|
|
|
case ir.OCALL:
|
2021-06-07 18:13:15 -07:00
|
|
|
call := n.(*ir.CallExpr)
|
|
|
|
|
if call.X.Op() == ir.OXDOT {
|
[dev.typeparams] Add optional sub-dict entry for typeparam bound calls
In the case that a generic function/method f does a method call on a
type param allowed by its bound, an instantiation of f may do a direct
method call of a concrete type or a method call defined on a generic
type, depending on whether the passed type in a concrete type or an
instantiated type with the appropriate method defined. See the test case
boundmethod.go added to this change.
In order to keep the dictionary format the same for all instantiations
of a generic function/method, I decided to have an optional
sub-dictionary entry for "bounds" calls. At the point that we are
creating the actual dictionary, we can then fill in the needed
sub-dictionary, if the type arg is an instantiated type, or a zeroed
dictionary entry, if type arg is not instantiated and the method will be
on a concrete type.
In order to implement this, I now fill in n.Selection for "bounds"
method calls in generic functions as well. Also, I need to calculate
n.Selection correctly during import for the case where it is now set -
method calls on generic types, and bounds calls on typeparams.
With this change, the dictionaries/sub-dictionaries are correct for
absdiff.go. The new test boundmethod.go illustrates the case where the
bound sub-dict entry is not used for a dictionary for stringify[myint],
but is used for a dictionary for stringify[StringInt[myint]].
Change-Id: Ie2bcb971b7019a9f1da68c97eb03da2333327457
Reviewed-on: https://go-review.googlesource.com/c/go/+/333456
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
2021-07-02 17:51:20 -07:00
|
|
|
var nameNode *ir.Name
|
|
|
|
|
se := call.X.(*ir.SelectorExpr)
|
|
|
|
|
if types.IsInterfaceMethod(se.Selection.Type) {
|
|
|
|
|
// This is a method call enabled by a type bound.
|
|
|
|
|
tmpse := ir.NewSelectorExpr(base.Pos, ir.OXDOT, se.X, se.Sel)
|
|
|
|
|
tmpse = typecheck.AddImplicitDots(tmpse)
|
|
|
|
|
tparam := tmpse.X.Type()
|
|
|
|
|
assert(tparam.IsTypeParam())
|
|
|
|
|
recvType := targs[tparam.Index()]
|
|
|
|
|
if len(recvType.RParams()) == 0 {
|
|
|
|
|
// No sub-dictionary entry is
|
|
|
|
|
// actually needed, since the
|
|
|
|
|
// typeparam is not an
|
|
|
|
|
// instantiated type that
|
|
|
|
|
// will have generic methods.
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
// This is a method call for an
|
|
|
|
|
// instantiated type, so we need a
|
|
|
|
|
// sub-dictionary.
|
|
|
|
|
targs := recvType.RParams()
|
|
|
|
|
genRecvType := recvType.OrigSym.Def.Type()
|
|
|
|
|
nameNode = typecheck.Lookdot1(call.X, se.Sel, genRecvType, genRecvType.Methods(), 1).Nname.(*ir.Name)
|
|
|
|
|
sym = g.getDictionarySym(nameNode, targs, true)
|
|
|
|
|
} else {
|
|
|
|
|
// This is the case of a normal
|
|
|
|
|
// method call on a generic type.
|
|
|
|
|
nameNode = call.X.(*ir.SelectorExpr).Selection.Nname.(*ir.Name)
|
|
|
|
|
subtargs := deref(call.X.(*ir.SelectorExpr).X.Type()).RParams()
|
|
|
|
|
s2targs := make([]*types.Type, len(subtargs))
|
|
|
|
|
for i, t := range subtargs {
|
|
|
|
|
s2targs[i] = subst.Typ(t)
|
|
|
|
|
}
|
|
|
|
|
sym = g.getDictionarySym(nameNode, s2targs, true)
|
2021-06-07 18:13:15 -07:00
|
|
|
}
|
|
|
|
|
} else {
|
2021-06-30 15:38:56 -07:00
|
|
|
inst := call.X.(*ir.InstExpr)
|
2021-06-07 18:13:15 -07:00
|
|
|
var nameNode *ir.Name
|
|
|
|
|
var meth *ir.SelectorExpr
|
|
|
|
|
var isMeth bool
|
|
|
|
|
if meth, isMeth = inst.X.(*ir.SelectorExpr); isMeth {
|
|
|
|
|
nameNode = meth.Selection.Nname.(*ir.Name)
|
|
|
|
|
} else {
|
|
|
|
|
nameNode = inst.X.(*ir.Name)
|
|
|
|
|
}
|
|
|
|
|
subtargs := typecheck.TypesOf(inst.Targs)
|
|
|
|
|
for i, t := range subtargs {
|
|
|
|
|
subtargs[i] = subst.Typ(t)
|
|
|
|
|
}
|
2021-06-26 18:18:16 -07:00
|
|
|
sym = g.getDictionarySym(nameNode, subtargs, isMeth)
|
2021-06-07 18:13:15 -07:00
|
|
|
}
|
2021-06-28 18:04:58 -07:00
|
|
|
|
|
|
|
|
case ir.OFUNCINST:
|
2021-06-07 18:13:15 -07:00
|
|
|
inst := n.(*ir.InstExpr)
|
|
|
|
|
nameNode := inst.X.(*ir.Name)
|
|
|
|
|
subtargs := typecheck.TypesOf(inst.Targs)
|
|
|
|
|
for i, t := range subtargs {
|
|
|
|
|
subtargs[i] = subst.Typ(t)
|
|
|
|
|
}
|
2021-06-26 18:18:16 -07:00
|
|
|
sym = g.getDictionarySym(nameNode, subtargs, false)
|
2021-06-28 18:04:58 -07:00
|
|
|
|
|
|
|
|
case ir.OXDOT:
|
2021-06-07 18:13:15 -07:00
|
|
|
selExpr := n.(*ir.SelectorExpr)
|
|
|
|
|
subtargs := selExpr.X.Type().RParams()
|
|
|
|
|
s2targs := make([]*types.Type, len(subtargs))
|
|
|
|
|
for i, t := range subtargs {
|
|
|
|
|
s2targs[i] = subst.Typ(t)
|
|
|
|
|
}
|
2021-06-30 15:38:56 -07:00
|
|
|
nameNode := selExpr.Selection.Nname.(*ir.Name)
|
|
|
|
|
sym = g.getDictionarySym(nameNode, s2targs, true)
|
2021-06-28 18:04:58 -07:00
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
assert(false)
|
2021-06-26 18:18:16 -07:00
|
|
|
}
|
2021-06-28 18:04:58 -07:00
|
|
|
|
[dev.typeparams] Add optional sub-dict entry for typeparam bound calls
In the case that a generic function/method f does a method call on a
type param allowed by its bound, an instantiation of f may do a direct
method call of a concrete type or a method call defined on a generic
type, depending on whether the passed type in a concrete type or an
instantiated type with the appropriate method defined. See the test case
boundmethod.go added to this change.
In order to keep the dictionary format the same for all instantiations
of a generic function/method, I decided to have an optional
sub-dictionary entry for "bounds" calls. At the point that we are
creating the actual dictionary, we can then fill in the needed
sub-dictionary, if the type arg is an instantiated type, or a zeroed
dictionary entry, if type arg is not instantiated and the method will be
on a concrete type.
In order to implement this, I now fill in n.Selection for "bounds"
method calls in generic functions as well. Also, I need to calculate
n.Selection correctly during import for the case where it is now set -
method calls on generic types, and bounds calls on typeparams.
With this change, the dictionaries/sub-dictionaries are correct for
absdiff.go. The new test boundmethod.go illustrates the case where the
bound sub-dict entry is not used for a dictionary for stringify[myint],
but is used for a dictionary for stringify[StringInt[myint]].
Change-Id: Ie2bcb971b7019a9f1da68c97eb03da2333327457
Reviewed-on: https://go-review.googlesource.com/c/go/+/333456
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
2021-07-02 17:51:20 -07:00
|
|
|
if sym == nil {
|
|
|
|
|
// Unused sub-dictionary entry, just emit 0.
|
|
|
|
|
off = objw.Uintptr(lsym, off, 0)
|
|
|
|
|
infoPrint(" - Unused subdict entry\n")
|
|
|
|
|
} else {
|
|
|
|
|
off = objw.SymPtr(lsym, off, sym.Linksym(), 0)
|
|
|
|
|
infoPrint(" - Subdict %v\n", sym.Name)
|
|
|
|
|
}
|
2021-06-07 18:13:15 -07:00
|
|
|
}
|
2021-06-26 18:18:16 -07:00
|
|
|
|
[dev.typeparams] cmd/compile: add dictionary entries for itab conversion
This fix the case where a type param or derived type is converted to a
non-empty interface. Previously, we were converting to an empty
interface and then using DOTTYPE to convert to the correct non-empty
interface. In that case, we can get the needed itab directly from the
dictionary. This is needed for correctness from shapes, if the
destination interface is parameterized, else we will incorrectly convert
to the shape version of the interface.
Creating/writing an itab can involve generating wrappers for a bunch of
methods, which may use dictionaries. So, all the
dictionaries/instantiations are being generated on the fly and have
recursive relationships, it is simplest to finish creating/writing the
itabs at the end of the stenciling phase. So, we create a list of the
dictionaries which need to be completed by writing out their itab
entries.
The existing tests ordered.go, ifaceconv.go, and issue44688.go make use
of this optimization.
Got itab conversions for bound calls working, except for 13.go.
Also, want to get rid of the concretify, but I think we need more info
on the Bound from types2.
Change-Id: If552958a7b8a435500d6cc42c401572c367b30d1
Reviewed-on: https://go-review.googlesource.com/c/go/+/336993
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2021-07-12 19:34:15 -07:00
|
|
|
delay := &delayInfo{
|
|
|
|
|
gf: gf,
|
|
|
|
|
targs: targs,
|
|
|
|
|
sym: sym,
|
|
|
|
|
off: off,
|
|
|
|
|
}
|
|
|
|
|
g.dictSymsToFinalize = append(g.dictSymsToFinalize, delay)
|
2021-06-26 18:18:16 -07:00
|
|
|
g.instTypeList = append(g.instTypeList, subst.InstTypeList...)
|
2021-06-07 18:13:15 -07:00
|
|
|
}
|
2021-06-26 18:18:16 -07:00
|
|
|
return sym
|
|
|
|
|
}
|
[dev.typeparams] cmd/compile: add dictionary entries for itab conversion
This fix the case where a type param or derived type is converted to a
non-empty interface. Previously, we were converting to an empty
interface and then using DOTTYPE to convert to the correct non-empty
interface. In that case, we can get the needed itab directly from the
dictionary. This is needed for correctness from shapes, if the
destination interface is parameterized, else we will incorrectly convert
to the shape version of the interface.
Creating/writing an itab can involve generating wrappers for a bunch of
methods, which may use dictionaries. So, all the
dictionaries/instantiations are being generated on the fly and have
recursive relationships, it is simplest to finish creating/writing the
itabs at the end of the stenciling phase. So, we create a list of the
dictionaries which need to be completed by writing out their itab
entries.
The existing tests ordered.go, ifaceconv.go, and issue44688.go make use
of this optimization.
Got itab conversions for bound calls working, except for 13.go.
Also, want to get rid of the concretify, but I think we need more info
on the Bound from types2.
Change-Id: If552958a7b8a435500d6cc42c401572c367b30d1
Reviewed-on: https://go-review.googlesource.com/c/go/+/336993
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2021-07-12 19:34:15 -07:00
|
|
|
|
|
|
|
|
// finalizeSyms finishes up all dictionaries on g.dictSymsToFinalize, by writing out
|
|
|
|
|
// any needed LSyms for itabs. The itab lsyms create wrappers which need various
|
|
|
|
|
// dictionaries and method instantiations to be complete, so, to avoid recursive
|
|
|
|
|
// dependencies, we finalize the itab lsyms only after all dictionaries syms and
|
|
|
|
|
// instantiations have been created.
|
|
|
|
|
func (g *irgen) finalizeSyms() {
|
|
|
|
|
for _, d := range g.dictSymsToFinalize {
|
|
|
|
|
lsym := d.sym.Linksym()
|
|
|
|
|
info := g.getGfInfo(d.gf)
|
|
|
|
|
|
|
|
|
|
subst := typecheck.Tsubster{
|
|
|
|
|
Tparams: info.tparams,
|
|
|
|
|
Targs: d.targs,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Emit an entry for each itab
|
|
|
|
|
for _, n := range info.itabConvs {
|
|
|
|
|
var srctype, dsttype *types.Type
|
|
|
|
|
if n.Op() == ir.OXDOT {
|
|
|
|
|
se := n.(*ir.SelectorExpr)
|
|
|
|
|
srctype = subst.Typ(se.X.Type())
|
|
|
|
|
dsttype = subst.Typ(se.X.Type().Bound())
|
2021-07-23 17:19:51 -07:00
|
|
|
found := false
|
|
|
|
|
for i, m := range dsttype.AllMethods().Slice() {
|
|
|
|
|
if se.Sel == m.Sym {
|
|
|
|
|
// Mark that this method se.Sel is
|
|
|
|
|
// used for the dsttype interface, so
|
|
|
|
|
// it won't get deadcoded.
|
|
|
|
|
reflectdata.MarkUsedIfaceMethodIndex(lsym, dsttype, i)
|
|
|
|
|
found = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
assert(found)
|
[dev.typeparams] cmd/compile: add dictionary entries for itab conversion
This fix the case where a type param or derived type is converted to a
non-empty interface. Previously, we were converting to an empty
interface and then using DOTTYPE to convert to the correct non-empty
interface. In that case, we can get the needed itab directly from the
dictionary. This is needed for correctness from shapes, if the
destination interface is parameterized, else we will incorrectly convert
to the shape version of the interface.
Creating/writing an itab can involve generating wrappers for a bunch of
methods, which may use dictionaries. So, all the
dictionaries/instantiations are being generated on the fly and have
recursive relationships, it is simplest to finish creating/writing the
itabs at the end of the stenciling phase. So, we create a list of the
dictionaries which need to be completed by writing out their itab
entries.
The existing tests ordered.go, ifaceconv.go, and issue44688.go make use
of this optimization.
Got itab conversions for bound calls working, except for 13.go.
Also, want to get rid of the concretify, but I think we need more info
on the Bound from types2.
Change-Id: If552958a7b8a435500d6cc42c401572c367b30d1
Reviewed-on: https://go-review.googlesource.com/c/go/+/336993
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2021-07-12 19:34:15 -07:00
|
|
|
} else {
|
|
|
|
|
assert(n.Op() == ir.OCONVIFACE)
|
|
|
|
|
srctype = subst.Typ(n.(*ir.ConvExpr).X.Type())
|
|
|
|
|
dsttype = subst.Typ(n.Type())
|
|
|
|
|
}
|
|
|
|
|
itabLsym := reflectdata.ITabLsym(srctype, dsttype)
|
|
|
|
|
d.off = objw.SymPtr(lsym, d.off, itabLsym, 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
objw.Global(lsym, int32(d.off), obj.DUPOK|obj.RODATA)
|
|
|
|
|
infoPrint("=== Finalized dictionary %s\n", d.sym.Name)
|
|
|
|
|
|
|
|
|
|
g.instTypeList = append(g.instTypeList, subst.InstTypeList...)
|
|
|
|
|
}
|
|
|
|
|
g.dictSymsToFinalize = nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-26 18:18:16 -07:00
|
|
|
func (g *irgen) getDictionaryValue(gf *ir.Name, targs []*types.Type, isMeth bool) ir.Node {
|
|
|
|
|
sym := g.getDictionarySym(gf, targs, isMeth)
|
2021-06-07 18:13:15 -07:00
|
|
|
|
|
|
|
|
// Make a node referencing the dictionary symbol.
|
|
|
|
|
n := typecheck.NewName(sym)
|
|
|
|
|
n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter
|
|
|
|
|
n.SetTypecheck(1)
|
|
|
|
|
n.Class = ir.PEXTERN
|
|
|
|
|
sym.Def = n
|
|
|
|
|
|
|
|
|
|
// Return the address of the dictionary.
|
|
|
|
|
np := typecheck.NodAddr(n)
|
|
|
|
|
// Note: treat dictionary pointers as uintptrs, so they aren't pointers
|
|
|
|
|
// with respect to GC. That saves on stack scanning work, write barriers, etc.
|
|
|
|
|
// We can get away with it because dictionaries are global variables.
|
|
|
|
|
// TODO: use a cast, or is typing directly ok?
|
|
|
|
|
np.SetType(types.Types[types.TUINTPTR])
|
|
|
|
|
np.SetTypecheck(1)
|
|
|
|
|
return np
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-28 18:04:58 -07:00
|
|
|
// hasTParamNodes returns true if the type of any node in targs has a typeparam.
|
|
|
|
|
func hasTParamNodes(targs []ir.Node) bool {
|
|
|
|
|
for _, n := range targs {
|
|
|
|
|
if n.Type().HasTParam() {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// hasTParamNodes returns true if any type in targs has a typeparam.
|
|
|
|
|
func hasTParamTypes(targs []*types.Type) bool {
|
|
|
|
|
for _, t := range targs {
|
|
|
|
|
if t.HasTParam() {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-07 18:13:15 -07:00
|
|
|
// getGfInfo get information for a generic function - type params, derived generic
|
|
|
|
|
// types, and subdictionaries.
|
|
|
|
|
func (g *irgen) getGfInfo(gn *ir.Name) *gfInfo {
|
|
|
|
|
infop := g.gfInfoMap[gn.Sym()]
|
|
|
|
|
if infop != nil {
|
|
|
|
|
return infop
|
|
|
|
|
}
|
|
|
|
|
|
[dev.typeparams] Add optional sub-dict entry for typeparam bound calls
In the case that a generic function/method f does a method call on a
type param allowed by its bound, an instantiation of f may do a direct
method call of a concrete type or a method call defined on a generic
type, depending on whether the passed type in a concrete type or an
instantiated type with the appropriate method defined. See the test case
boundmethod.go added to this change.
In order to keep the dictionary format the same for all instantiations
of a generic function/method, I decided to have an optional
sub-dictionary entry for "bounds" calls. At the point that we are
creating the actual dictionary, we can then fill in the needed
sub-dictionary, if the type arg is an instantiated type, or a zeroed
dictionary entry, if type arg is not instantiated and the method will be
on a concrete type.
In order to implement this, I now fill in n.Selection for "bounds"
method calls in generic functions as well. Also, I need to calculate
n.Selection correctly during import for the case where it is now set -
method calls on generic types, and bounds calls on typeparams.
With this change, the dictionaries/sub-dictionaries are correct for
absdiff.go. The new test boundmethod.go illustrates the case where the
bound sub-dict entry is not used for a dictionary for stringify[myint],
but is used for a dictionary for stringify[StringInt[myint]].
Change-Id: Ie2bcb971b7019a9f1da68c97eb03da2333327457
Reviewed-on: https://go-review.googlesource.com/c/go/+/333456
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
2021-07-02 17:51:20 -07:00
|
|
|
checkFetchBody(gn)
|
2021-06-07 18:13:15 -07:00
|
|
|
var info gfInfo
|
|
|
|
|
gf := gn.Func
|
|
|
|
|
recv := gf.Type().Recv()
|
|
|
|
|
if recv != nil {
|
|
|
|
|
info.tparams = deref(recv.Type).RParams()
|
|
|
|
|
} else {
|
2021-06-28 18:04:58 -07:00
|
|
|
tparams := gn.Type().TParams().FieldSlice()
|
|
|
|
|
info.tparams = make([]*types.Type, len(tparams))
|
|
|
|
|
for i, f := range tparams {
|
2021-06-07 18:13:15 -07:00
|
|
|
info.tparams[i] = f.Type
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, n := range gf.Dcl {
|
|
|
|
|
addType(&info, n, n.Type())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if infoPrintMode {
|
2021-06-28 18:04:58 -07:00
|
|
|
fmt.Printf(">>> GfInfo for %v\n", gn)
|
2021-06-07 18:13:15 -07:00
|
|
|
for _, t := range info.tparams {
|
|
|
|
|
fmt.Printf(" Typeparam %v\n", t)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-28 18:04:58 -07:00
|
|
|
var visitFunc func(ir.Node)
|
|
|
|
|
visitFunc = func(n ir.Node) {
|
|
|
|
|
if n.Op() == ir.OFUNCINST && !n.(*ir.InstExpr).Implicit() {
|
|
|
|
|
if hasTParamNodes(n.(*ir.InstExpr).Targs) {
|
2021-06-07 18:13:15 -07:00
|
|
|
infoPrint(" Closure&subdictionary required at generic function value %v\n", n.(*ir.InstExpr).X)
|
|
|
|
|
info.subDictCalls = append(info.subDictCalls, n)
|
2021-06-28 18:04:58 -07:00
|
|
|
}
|
|
|
|
|
} else if n.Op() == ir.OXDOT && !n.(*ir.SelectorExpr).Implicit() &&
|
|
|
|
|
n.(*ir.SelectorExpr).Selection != nil &&
|
|
|
|
|
len(n.(*ir.SelectorExpr).X.Type().RParams()) > 0 {
|
|
|
|
|
if n.(*ir.SelectorExpr).X.Op() == ir.OTYPE {
|
|
|
|
|
infoPrint(" Closure&subdictionary required at generic meth expr %v\n", n)
|
|
|
|
|
} else {
|
|
|
|
|
infoPrint(" Closure&subdictionary required at generic meth value %v\n", n)
|
|
|
|
|
}
|
|
|
|
|
if hasTParamTypes(deref(n.(*ir.SelectorExpr).X.Type()).RParams()) {
|
2021-06-30 15:38:56 -07:00
|
|
|
if n.(*ir.SelectorExpr).X.Op() == ir.OTYPE {
|
|
|
|
|
infoPrint(" Closure&subdictionary required at generic meth expr %v\n", n)
|
|
|
|
|
} else {
|
|
|
|
|
infoPrint(" Closure&subdictionary required at generic meth value %v\n", n)
|
2021-06-07 18:13:15 -07:00
|
|
|
}
|
2021-06-30 15:38:56 -07:00
|
|
|
info.subDictCalls = append(info.subDictCalls, n)
|
2021-06-07 18:13:15 -07:00
|
|
|
}
|
2021-06-28 18:04:58 -07:00
|
|
|
}
|
|
|
|
|
if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST {
|
|
|
|
|
n.(*ir.CallExpr).X.(*ir.InstExpr).SetImplicit(true)
|
|
|
|
|
if hasTParamNodes(n.(*ir.CallExpr).X.(*ir.InstExpr).Targs) {
|
|
|
|
|
infoPrint(" Subdictionary at generic function/method call: %v - %v\n", n.(*ir.CallExpr).X.(*ir.InstExpr).X, n)
|
2021-06-07 18:13:15 -07:00
|
|
|
info.subDictCalls = append(info.subDictCalls, n)
|
|
|
|
|
}
|
2021-06-28 18:04:58 -07:00
|
|
|
}
|
|
|
|
|
if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OXDOT &&
|
|
|
|
|
n.(*ir.CallExpr).X.(*ir.SelectorExpr).Selection != nil &&
|
|
|
|
|
len(deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams()) > 0 {
|
|
|
|
|
n.(*ir.CallExpr).X.(*ir.SelectorExpr).SetImplicit(true)
|
|
|
|
|
if hasTParamTypes(deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).RParams()) {
|
2021-06-07 18:13:15 -07:00
|
|
|
infoPrint(" Subdictionary at generic method call: %v\n", n)
|
|
|
|
|
info.subDictCalls = append(info.subDictCalls, n)
|
|
|
|
|
}
|
2021-06-28 18:04:58 -07:00
|
|
|
}
|
[dev.typeparams] Add optional sub-dict entry for typeparam bound calls
In the case that a generic function/method f does a method call on a
type param allowed by its bound, an instantiation of f may do a direct
method call of a concrete type or a method call defined on a generic
type, depending on whether the passed type in a concrete type or an
instantiated type with the appropriate method defined. See the test case
boundmethod.go added to this change.
In order to keep the dictionary format the same for all instantiations
of a generic function/method, I decided to have an optional
sub-dictionary entry for "bounds" calls. At the point that we are
creating the actual dictionary, we can then fill in the needed
sub-dictionary, if the type arg is an instantiated type, or a zeroed
dictionary entry, if type arg is not instantiated and the method will be
on a concrete type.
In order to implement this, I now fill in n.Selection for "bounds"
method calls in generic functions as well. Also, I need to calculate
n.Selection correctly during import for the case where it is now set -
method calls on generic types, and bounds calls on typeparams.
With this change, the dictionaries/sub-dictionaries are correct for
absdiff.go. The new test boundmethod.go illustrates the case where the
bound sub-dict entry is not used for a dictionary for stringify[myint],
but is used for a dictionary for stringify[StringInt[myint]].
Change-Id: Ie2bcb971b7019a9f1da68c97eb03da2333327457
Reviewed-on: https://go-review.googlesource.com/c/go/+/333456
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
2021-07-02 17:51:20 -07:00
|
|
|
if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OXDOT &&
|
|
|
|
|
n.(*ir.CallExpr).X.(*ir.SelectorExpr).Selection != nil &&
|
|
|
|
|
deref(n.(*ir.CallExpr).X.(*ir.SelectorExpr).X.Type()).IsTypeParam() {
|
|
|
|
|
n.(*ir.CallExpr).X.(*ir.SelectorExpr).SetImplicit(true)
|
|
|
|
|
infoPrint(" Optional subdictionary at generic bound call: %v\n", n)
|
|
|
|
|
info.subDictCalls = append(info.subDictCalls, n)
|
|
|
|
|
}
|
[dev.typeparams] cmd/compile: add dictionary entries for itab conversion
This fix the case where a type param or derived type is converted to a
non-empty interface. Previously, we were converting to an empty
interface and then using DOTTYPE to convert to the correct non-empty
interface. In that case, we can get the needed itab directly from the
dictionary. This is needed for correctness from shapes, if the
destination interface is parameterized, else we will incorrectly convert
to the shape version of the interface.
Creating/writing an itab can involve generating wrappers for a bunch of
methods, which may use dictionaries. So, all the
dictionaries/instantiations are being generated on the fly and have
recursive relationships, it is simplest to finish creating/writing the
itabs at the end of the stenciling phase. So, we create a list of the
dictionaries which need to be completed by writing out their itab
entries.
The existing tests ordered.go, ifaceconv.go, and issue44688.go make use
of this optimization.
Got itab conversions for bound calls working, except for 13.go.
Also, want to get rid of the concretify, but I think we need more info
on the Bound from types2.
Change-Id: If552958a7b8a435500d6cc42c401572c367b30d1
Reviewed-on: https://go-review.googlesource.com/c/go/+/336993
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
2021-07-12 19:34:15 -07:00
|
|
|
if n.Op() == ir.OCONVIFACE && n.Type().IsInterface() &&
|
|
|
|
|
!n.Type().IsEmptyInterface() &&
|
|
|
|
|
n.(*ir.ConvExpr).X.Type().HasTParam() {
|
|
|
|
|
infoPrint(" Itab for interface conv: %v\n", n)
|
|
|
|
|
info.itabConvs = append(info.itabConvs, n)
|
|
|
|
|
}
|
|
|
|
|
if n.Op() == ir.OXDOT && n.(*ir.SelectorExpr).X.Type().IsTypeParam() {
|
|
|
|
|
infoPrint(" Itab for interface conv: %v\n", n)
|
|
|
|
|
info.itabConvs = append(info.itabConvs, n)
|
|
|
|
|
}
|
2021-06-28 18:04:58 -07:00
|
|
|
if n.Op() == ir.OCLOSURE {
|
|
|
|
|
// Visit the closure body and add all relevant entries to the
|
|
|
|
|
// dictionary of the outer function (closure will just use
|
|
|
|
|
// the dictionary of the outer function).
|
|
|
|
|
for _, n1 := range n.(*ir.ClosureExpr).Func.Body {
|
|
|
|
|
ir.Visit(n1, visitFunc)
|
2021-06-07 18:13:15 -07:00
|
|
|
}
|
2021-06-28 18:04:58 -07:00
|
|
|
}
|
|
|
|
|
addType(&info, n, n.Type())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, stmt := range gf.Body {
|
|
|
|
|
ir.Visit(stmt, visitFunc)
|
|
|
|
|
}
|
|
|
|
|
if infoPrintMode {
|
|
|
|
|
for _, t := range info.derivedTypes {
|
|
|
|
|
fmt.Printf(" Derived type %v\n", t)
|
|
|
|
|
}
|
|
|
|
|
fmt.Printf(">>> Done Gfinfo\n")
|
2021-06-07 18:13:15 -07:00
|
|
|
}
|
|
|
|
|
g.gfInfoMap[gn.Sym()] = &info
|
|
|
|
|
return &info
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// addType adds t to info.derivedTypes if it is parameterized type (which is not
|
|
|
|
|
// just a simple type param) that is different from any existing type on
|
|
|
|
|
// info.derivedTypes.
|
|
|
|
|
func addType(info *gfInfo, n ir.Node, t *types.Type) {
|
|
|
|
|
if t == nil || !t.HasTParam() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if t.IsTypeParam() && t.Underlying() == t {
|
|
|
|
|
return
|
|
|
|
|
}
|
2021-07-21 19:17:20 -07:00
|
|
|
if t.Kind() == types.TFUNC && n != nil &&
|
|
|
|
|
(t.Recv() != nil ||
|
|
|
|
|
n.Op() == ir.ONAME && n.Name().Class == ir.PFUNC) {
|
|
|
|
|
// Don't use the type of a named generic function or method,
|
|
|
|
|
// since that is parameterized by other typeparams.
|
|
|
|
|
// (They all come from arguments of a FUNCINST node.)
|
2021-06-07 18:13:15 -07:00
|
|
|
return
|
|
|
|
|
}
|
2021-07-21 19:17:20 -07:00
|
|
|
if doubleCheck && !parameterizedBy(t, info.tparams) {
|
|
|
|
|
base.Fatalf("adding type with invalid parameters %+v", t)
|
|
|
|
|
}
|
2021-06-07 18:13:15 -07:00
|
|
|
if t.Kind() == types.TSTRUCT && t.IsFuncArgStruct() {
|
|
|
|
|
// Multiple return values are not a relevant new type (?).
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// Ignore a derived type we've already added.
|
|
|
|
|
for _, et := range info.derivedTypes {
|
|
|
|
|
if types.Identical(t, et) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
info.derivedTypes = append(info.derivedTypes, t)
|
|
|
|
|
}
|
2021-07-20 14:52:19 -07:00
|
|
|
|
|
|
|
|
// parameterizedBy returns true if t is parameterized by (at most) params.
|
|
|
|
|
func parameterizedBy(t *types.Type, params []*types.Type) bool {
|
|
|
|
|
return parameterizedBy1(t, params, map[*types.Type]bool{})
|
|
|
|
|
}
|
|
|
|
|
func parameterizedBy1(t *types.Type, params []*types.Type, visited map[*types.Type]bool) bool {
|
|
|
|
|
if visited[t] {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
visited[t] = true
|
2021-07-21 19:17:20 -07:00
|
|
|
|
|
|
|
|
if t.Sym() != nil && len(t.RParams()) > 0 {
|
|
|
|
|
// This defined type is instantiated. Check the instantiating types.
|
|
|
|
|
for _, r := range t.RParams() {
|
|
|
|
|
if !parameterizedBy1(r, params, visited) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
2021-07-20 14:52:19 -07:00
|
|
|
switch t.Kind() {
|
|
|
|
|
case types.TTYPEPARAM:
|
2021-07-21 19:17:20 -07:00
|
|
|
// Check if t is one of the allowed parameters in scope.
|
2021-07-20 14:52:19 -07:00
|
|
|
for _, p := range params {
|
|
|
|
|
if p == t {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-21 19:17:20 -07:00
|
|
|
// Couldn't find t in the list of allowed parameters.
|
2021-07-20 14:52:19 -07:00
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
case types.TARRAY, types.TPTR, types.TSLICE, types.TCHAN:
|
|
|
|
|
return parameterizedBy1(t.Elem(), params, visited)
|
|
|
|
|
|
|
|
|
|
case types.TMAP:
|
|
|
|
|
return parameterizedBy1(t.Key(), params, visited) && parameterizedBy1(t.Elem(), params, visited)
|
|
|
|
|
|
|
|
|
|
case types.TFUNC:
|
2021-07-21 19:17:20 -07:00
|
|
|
return parameterizedBy1(t.TParams(), params, visited) && parameterizedBy1(t.Recvs(), params, visited) && parameterizedBy1(t.Params(), params, visited) && parameterizedBy1(t.Results(), params, visited)
|
2021-07-20 14:52:19 -07:00
|
|
|
|
|
|
|
|
case types.TSTRUCT:
|
|
|
|
|
for _, f := range t.Fields().Slice() {
|
|
|
|
|
if !parameterizedBy1(f.Type, params, visited) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
case types.TINTER:
|
|
|
|
|
for _, f := range t.Methods().Slice() {
|
|
|
|
|
if !parameterizedBy1(f.Type, params, visited) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
case types.TINT, types.TINT8, types.TINT16, types.TINT32, types.TINT64,
|
|
|
|
|
types.TUINT, types.TUINT8, types.TUINT16, types.TUINT32, types.TUINT64,
|
|
|
|
|
types.TUINTPTR, types.TBOOL, types.TSTRING, types.TFLOAT32, types.TFLOAT64, types.TCOMPLEX64, types.TCOMPLEX128:
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
base.Fatalf("bad type kind %+v", t)
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|