// markType recursively visits types reachable from t to identify
// functions whose inline bodies may be needed.
func (p *exporter) markType(t *types.Type) {
- if p.marked[t] {
+ if t.IsInstantiatedGeneric() {
+ // Re-instantiated types don't add anything new, so don't follow them.
return
}
- p.marked[t] = true
- if t.HasTParam() {
- // Don't deal with any generic types or their methods, since we
- // will only be inlining actual instantiations, not generic methods.
+ if p.marked[t] {
return
}
+ p.marked[t] = true
// If this is a named type, mark all of its associated
// methods. Skip interface types because t.Methods contains
p.markType(f.Type)
}
}
+
+ case types.TTYPEPARAM:
+ // No other type that needs to be followed.
}
}
}
g.funcBody(fn, decl.Recv, decl.Type, decl.Body)
+ if fn.Type().HasTParam() && fn.Body != nil {
+ // Set pointers to the dcls/body of a generic function/method in
+ // the Inl struct, so it is marked for export, is available for
+ // stenciling, and works with Inline_Flood().
+ fn.Inl = &ir.Inline{
+ Cost: 1,
+ Dcl: fn.Dcl,
+ Body: fn.Body,
+ }
+ }
out.Append(fn)
}
"go/constant"
)
-// For catching problems as we add more features
-// TODO(danscales): remove assertions or replace with base.FatalfAt()
func assert(p bool) {
if !p {
panic("assertion failed")
// in the local package, even if they may be marked as part of
// another package (the package of their base generic type).
if tbase.Sym() != nil && tbase.Sym().Pkg != types.LocalPkg &&
- !tbase.IsInstantiated() {
+ !tbase.IsFullyInstantiated() {
if i := typecheck.BaseTypeIndex(t); i >= 0 {
lsym.Pkg = tbase.Sym().Pkg.Prefix
lsym.SymIdx = int32(i)
// instantiated methods.
if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type &&
rcvr.Elem().Sym() != nil && rcvr.Elem().Sym().Pkg != types.LocalPkg &&
- !rcvr.Elem().IsInstantiated() {
+ !rcvr.Elem().IsFullyInstantiated() {
return lsym
}
f := t.Field(i)
s := f.Sym
- if s != nil && !types.IsExported(s.Name) && s.Pkg != types.LocalPkg {
- base.Errorf("implicit assignment of unexported field '%s' in %v literal", s.Name, t)
+
+ // Do the test for assigning to unexported fields.
+ // But if this is an instantiated function, then
+ // the function has already been typechecked. In
+ // that case, don't do the test, since it can fail
+ // for the closure structs created in
+ // walkClosure(), because the instantiated
+ // function is compiled as if in the source
+ // package of the generic function.
+ if !(ir.CurFunc != nil && strings.Index(ir.CurFunc.Nname.Sym().Name, "[") >= 0) {
+ if s != nil && !types.IsExported(s.Name) && s.Pkg != types.LocalPkg {
+ base.Errorf("implicit assignment of unexported field '%s' in %v literal", s.Name, t)
+ }
}
// No pushtype allowed here. Must name fields for that.
n1 = AssignConv(n1, f.Type, "field value")
// The information appears in the binary in the form of type descriptors;
// the struct is unnamed so that closures in multiple packages with the
// same struct type can share the descriptor.
+
+ // Make sure the .F field is in the same package as the rest of the
+ // fields. This deals with closures in instantiated functions, which are
+ // compiled as if from the source package of the generic function.
+ var pkg *types.Pkg
+ if len(clo.Func.ClosureVars) == 0 {
+ pkg = types.LocalPkg
+ } else {
+ for _, v := range clo.Func.ClosureVars {
+ if pkg == nil {
+ pkg = v.Sym().Pkg
+ } else if pkg != v.Sym().Pkg {
+ base.Fatalf("Closure variables from multiple packages")
+ }
+ }
+ }
+
fields := []*types.Field{
- types.NewField(base.Pos, Lookup(".F"), types.Types[types.TUINTPTR]),
+ types.NewField(base.Pos, pkg.Lookup(".F"), types.Types[types.TUINTPTR]),
}
for _, v := range clo.Func.ClosureVars {
typ := v.Type()
}
}
- // Inline body.
- if n.Type().HasTParam() {
- if n.Func.Inl != nil {
- // n.Func.Inl may already be set on a generic function if
- // we imported it from another package, but shouldn't be
- // set for a generic function in the local package.
- if n.Sym().Pkg == types.LocalPkg {
- base.FatalfAt(n.Pos(), "generic function is marked inlineable")
- }
- } else {
- // Populate n.Func.Inl, so body of exported generic function will
- // be written out.
- n.Func.Inl = &ir.Inline{
- Cost: 1,
- Dcl: n.Func.Dcl,
- Body: n.Func.Body,
- }
- }
+ // Write out inline body or body of a generic function/method.
+ if n.Type().HasTParam() && n.Func.Body != nil && n.Func.Inl == nil {
+ base.FatalfAt(n.Pos(), "generic function is not marked inlineable")
}
if n.Func.Inl != nil {
w.uint64(1 + uint64(n.Func.Inl.Cost))
"cmd/compile/internal/base"
"cmd/internal/src"
"fmt"
+ "strings"
"sync"
)
}
}
-// IsInstantiated reports whether t is a fully instantiated generic type; i.e. an
+// IsBaseGeneric returns true if t is a generic type (not reinstantiated with
+// another type params or fully instantiated.
+func (t *Type) IsBaseGeneric() bool {
+ return len(t.RParams()) > 0 && strings.Index(t.Sym().Name, "[") < 0
+}
+
+// IsInstantiatedGeneric returns t if t ia generic type that has been
+// reinstantiated with new typeparams (i.e. is not fully instantiated).
+func (t *Type) IsInstantiatedGeneric() bool {
+ return len(t.RParams()) > 0 && strings.Index(t.Sym().Name, "[") >= 0 &&
+ t.HasTParam()
+}
+
+// IsFullyInstantiated reports whether t is a fully instantiated generic type; i.e. an
// instantiated generic type where all type arguments are non-generic or fully
// instantiated generic types.
-func (t *Type) IsInstantiated() bool {
+func (t *Type) IsFullyInstantiated() bool {
return len(t.RParams()) > 0 && !t.HasTParam()
}
}
// iterate returns an iterator that traverses the map.
-// func (m *Map[K, V]) Iterate() *Iterator[K, V] {
-// sender, receiver := Ranger[keyValue[K, V]]()
-// var f func(*node[K, V]) bool
-// f = func(n *node[K, V]) bool {
-// if n == nil {
-// return true
-// }
-// // Stop the traversal if Send fails, which means that
-// // nothing is listening to the receiver.
-// return f(n.left) &&
-// sender.Send(context.Background(), keyValue[K, V]{n.key, n.val}) &&
-// f(n.right)
-// }
-// go func() {
-// f(m.root)
-// sender.Close()
-// }()
-// return &Iterator[K, V]{receiver}
-// }
+func (m *Map[K, V]) Iterate() *Iterator[K, V] {
+ sender, receiver := Ranger[keyValue[K, V]]()
+ var f func(*node[K, V]) bool
+ f = func(n *node[K, V]) bool {
+ if n == nil {
+ return true
+ }
+ // Stop the traversal if Send fails, which means that
+ // nothing is listening to the receiver.
+ return f(n.left) &&
+ sender.Send(context.Background(), keyValue[K, V]{n.key, n.val}) &&
+ f(n.right)
+ }
+ go func() {
+ f(m.root)
+ sender.Close()
+ }()
+ return &Iterator[K, V]{receiver}
+}
// Iterator is used to iterate over the map.
type Iterator[K, V any] struct {
panic(fmt.Sprintf("unexpectedly found %q", []byte("d")))
}
- // TODO(danscales): Iterate() has some things to be fixed with inlining in
- // stenciled functions and using closures across packages.
-
- // gather := func(it *a.Iterator[[]byte, int]) []int {
- // var r []int
- // for {
- // _, v, ok := it.Next()
- // if !ok {
- // return r
- // }
- // r = append(r, v)
- // }
- // }
- // got := gather(m.Iterate())
- // want := []int{'a', 'b', 'x'}
- // if !a.SliceEqual(got, want) {
- // panic(fmt.Sprintf("Iterate returned %v, want %v", got, want))
- // }
+ gather := func(it *a.Iterator[[]byte, int]) []int {
+ var r []int
+ for {
+ _, v, ok := it.Next()
+ if !ok {
+ return r
+ }
+ r = append(r, v)
+ }
+ }
+ got := gather(m.Iterate())
+ want := []int{'a', 'b', 'x'}
+ if !a.SliceEqual(got, want) {
+ panic(fmt.Sprintf("Iterate returned %v, want %v", got, want))
+ }
}