// 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. package noder import ( "bytes" "cmd/compile/internal/base" "cmd/compile/internal/ir" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" "cmd/compile/internal/types2" "cmd/internal/src" "strings" ) func (g *irgen) pkg(pkg *types2.Package) *types.Pkg { switch pkg { case nil: return types.BuiltinPkg case g.self: return types.LocalPkg case types2.Unsafe: return types.UnsafePkg } return types.NewPkg(pkg.Path(), pkg.Name()) } // typ converts a types2.Type to a types.Type, including caching of previously // translated types. func (g *irgen) typ(typ types2.Type) *types.Type { // Defer the CheckSize calls until we have fully-defined a // (possibly-recursive) top-level type. types.DeferCheckSize() res := g.typ1(typ) types.ResumeCheckSize() // Finish up any types on typesToFinalize, now that we are at the top of a // fully-defined (possibly recursive) type. fillinMethods could create more // types to finalize. for len(g.typesToFinalize) > 0 { l := len(g.typesToFinalize) info := g.typesToFinalize[l-1] g.typesToFinalize = g.typesToFinalize[:l-1] g.fillinMethods(info.typ, info.ntyp) } return res } // typ1 is like typ, but doesn't call CheckSize, since it may have only // constructed part of a recursive type. Should not be called from outside this // file (g.typ is the "external" entry point). func (g *irgen) typ1(typ types2.Type) *types.Type { // Cache type2-to-type mappings. Important so that each defined generic // type (instantiated or not) has a single types.Type representation. // Also saves a lot of computation and memory by avoiding re-translating // types2 types repeatedly. res, ok := g.typs[typ] if !ok { res = g.typ0(typ) // Calculate the size for all concrete types seen by the frontend. // This is the replacement for the CheckSize() calls in the types1 // typechecker. These will be deferred until the top-level g.typ(). if res != nil && !res.IsUntyped() && !res.IsFuncArgStruct() && !res.HasTParam() { types.CheckSize(res) } g.typs[typ] = res } return res } // instTypeName2 creates a name for an instantiated type, base on the type args // (given as types2 types). func instTypeName2(name string, targs *types2.TypeList) string { b := bytes.NewBufferString(name) b.WriteByte('[') n := targs.Len() for i := 0; i < n; i++ { targ := targs.At(i) if i > 0 { b.WriteByte(',') } // Include package names for all types, including typeparams, to // make sure type arguments are uniquely specified. tname := types2.TypeString(targ, func(pkg *types2.Package) string { return pkg.Name() }) if strings.Index(tname, ", ") >= 0 { // types2.TypeString puts spaces after a comma in a type // list, but we don't want spaces in our actual type names // and method/function names derived from them. tname = strings.Replace(tname, ", ", ",", -1) } b.WriteString(tname) } b.WriteByte(']') return b.String() } // typ0 converts a types2.Type to a types.Type, but doesn't do the caching check // at the top level. func (g *irgen) typ0(typ types2.Type) *types.Type { switch typ := typ.(type) { case *types2.Basic: return g.basic(typ) case *types2.Named: // If tparams is set, but targs is not, typ is a base generic // type. typ is appearing as part of the source type of an alias, // since that is the only use of a generic type that doesn't // involve instantiation. We just translate the named type in the // normal way below using g.obj(). if typ.TParams() != nil && typ.TArgs() != nil { // typ is an instantiation of a defined (named) generic type. // This instantiation should also be a defined (named) type. // types2 gives us the substituted type in t.Underlying() // The substituted type may or may not still have type // params. We might, for example, be substituting one type // param for another type param. // // When converted to types.Type, typ has a unique name, // based on the names of the type arguments. instName := instTypeName2(typ.Obj().Name(), typ.TArgs()) s := g.pkg(typ.Obj().Pkg()).Lookup(instName) if s.Def != nil { // We have already encountered this instantiation. // Use the type we previously created, since there // must be exactly one instance of a defined type. return s.Def.Type() } // Make sure the base generic type exists in type1 (it may // not yet if we are referecing an imported generic type, as // opposed to a generic type declared in this package). _ = g.obj(typ.Orig().Obj()) // Create a forwarding type first and put it in the g.typs // map, in order to deal with recursive generic types // (including via method signatures). Set up the extra // ntyp information (Def, RParams, which may set // HasTParam) before translating the underlying type // itself, so we handle recursion correctly. ntyp := typecheck.NewIncompleteNamedType(g.pos(typ.Obj().Pos()), s) g.typs[typ] = ntyp // If ntyp still has type params, then we must be // referencing something like 'value[T2]', as when // specifying the generic receiver of a method, where // value was defined as "type value[T any] ...". Save the // type args, which will now be the new typeparams of the // current type. // // If ntyp does not have type params, we are saving the // non-generic types used to instantiate this type. We'll // use these when instantiating the methods of the // instantiated type. targs := typ.TArgs() rparams := make([]*types.Type, targs.Len()) for i := range rparams { rparams[i] = g.typ1(targs.At(i)) } ntyp.SetRParams(rparams) //fmt.Printf("Saw new type %v %v\n", instName, ntyp.HasTParam()) // Save the symbol for the base generic type. ntyp.OrigSym = g.pkg(typ.Obj().Pkg()).Lookup(typ.Obj().Name()) ntyp.SetUnderlying(g.typ1(typ.Underlying())) if typ.NumMethods() != 0 { // Save a delayed call to g.fillinMethods() (once // potentially recursive types have been fully // resolved). g.typesToFinalize = append(g.typesToFinalize, &typeDelayInfo{ typ: typ, ntyp: ntyp, }) } return ntyp } obj := g.obj(typ.Obj()) if obj.Op() != ir.OTYPE { base.FatalfAt(obj.Pos(), "expected type: %L", obj) } return obj.Type() case *types2.Array: return types.NewArray(g.typ1(typ.Elem()), typ.Len()) case *types2.Chan: return types.NewChan(g.typ1(typ.Elem()), dirs[typ.Dir()]) case *types2.Map: return types.NewMap(g.typ1(typ.Key()), g.typ1(typ.Elem())) case *types2.Pointer: return types.NewPtr(g.typ1(typ.Elem())) case *types2.Signature: return g.signature(nil, typ) case *types2.Slice: return types.NewSlice(g.typ1(typ.Elem())) case *types2.Struct: fields := make([]*types.Field, typ.NumFields()) for i := range fields { v := typ.Field(i) f := types.NewField(g.pos(v), g.selector(v), g.typ1(v.Type())) f.Note = typ.Tag(i) if v.Embedded() { f.Embedded = 1 } fields[i] = f } return types.NewStruct(g.tpkg(typ), fields) case *types2.Interface: embeddeds := make([]*types.Field, typ.NumEmbeddeds()) j := 0 for i := range embeddeds { // TODO(mdempsky): Get embedding position. e := typ.EmbeddedType(i) // With Go 1.18, an embedded element can be any type, not // just an interface. embeddeds[j] = types.NewField(src.NoXPos, nil, g.typ1(e)) j++ } embeddeds = embeddeds[:j] methods := make([]*types.Field, typ.NumExplicitMethods()) for i := range methods { m := typ.ExplicitMethod(i) mtyp := g.signature(types.FakeRecv(), m.Type().(*types2.Signature)) methods[i] = types.NewField(g.pos(m), g.selector(m), mtyp) } return types.NewInterface(g.tpkg(typ), append(embeddeds, methods...)) case *types2.TypeParam: // Save the name of the type parameter in the sym of the type. // Include the types2 subscript in the sym name pkg := g.tpkg(typ) sym := pkg.Lookup(types2.TypeString(typ, func(*types2.Package) string { return "" })) if sym.Def != nil { // Make sure we use the same type param type for the same // name, whether it is created during types1-import or // this types2-to-types1 translation. return sym.Def.Type() } tp := types.NewTypeParam(sym, typ.Index()) nname := ir.NewDeclNameAt(g.pos(typ.Obj().Pos()), ir.OTYPE, sym) sym.Def = nname nname.SetType(tp) tp.SetNod(nname) // Set g.typs[typ] in case the bound methods reference typ. g.typs[typ] = tp bound := g.typ1(typ.Constraint()) tp.SetBound(bound) return tp case *types2.Union: nt := typ.Len() tlist := make([]*types.Type, nt) tildes := make([]bool, nt) for i := range tlist { t := typ.Term(i) tlist[i] = g.typ1(t.Type()) tildes[i] = t.Tilde() } return types.NewUnion(tlist, tildes) case *types2.Tuple: // Tuples are used for the type of a function call (i.e. the // return value of the function). if typ == nil { return (*types.Type)(nil) } fields := make([]*types.Field, typ.Len()) for i := range fields { fields[i] = g.param(typ.At(i)) } t := types.NewStruct(types.LocalPkg, fields) t.StructType().Funarg = types.FunargResults return t default: base.FatalfAt(src.NoXPos, "unhandled type: %v (%T)", typ, typ) panic("unreachable") } } // fillinMethods fills in the method name nodes and types for a defined type with at // least one method. This is needed for later typechecking when looking up methods of // instantiated types, and for actually generating the methods for instantiated // types. func (g *irgen) fillinMethods(typ *types2.Named, ntyp *types.Type) { targs2 := typ.TArgs() targs := make([]*types.Type, targs2.Len()) for i := range targs { targs[i] = g.typ1(targs2.At(i)) } methods := make([]*types.Field, typ.NumMethods()) for i := range methods { m := typ.Method(i) recvType := deref2(types2.AsSignature(m.Type()).Recv().Type()) var meth *ir.Name if m.Pkg() != g.self { // Imported methods cannot be loaded by name (what // g.obj() does) - they must be loaded via their // type. meth = g.obj(recvType.(*types2.Named).Obj()).Type().Methods().Index(i).Nname.(*ir.Name) } else { meth = g.obj(m) } if recvType != types2.Type(typ) { // Unfortunately, meth is the type of the method of the // generic type, so we have to do a substitution to get // the name/type of the method of the instantiated type, // using m.Type().RParams() and typ.TArgs() inst2 := instTypeName2("", typ.TArgs()) name := meth.Sym().Name i1 := strings.Index(name, "[") i2 := strings.Index(name[i1:], "]") assert(i1 >= 0 && i2 >= 0) // Generate the name of the instantiated method. name = name[0:i1] + inst2 + name[i1+i2+1:] newsym := meth.Sym().Pkg.Lookup(name) var meth2 *ir.Name if newsym.Def != nil { meth2 = newsym.Def.(*ir.Name) } else { meth2 = ir.NewNameAt(meth.Pos(), newsym) rparams := types2.AsSignature(m.Type()).RParams() tparams := make([]*types.Type, rparams.Len()) for i := range tparams { tparams[i] = g.typ1(rparams.At(i)) } assert(len(tparams) == len(targs)) ts := typecheck.Tsubster{ Tparams: tparams, Targs: targs, } // Do the substitution of the type meth2.SetType(ts.Typ(meth.Type())) // Add any new fully instantiated types // seen during the substitution to // g.instTypeList. g.instTypeList = append(g.instTypeList, ts.InstTypeList...) newsym.Def = meth2 } meth = meth2 } methods[i] = types.NewField(meth.Pos(), g.selector(m), meth.Type()) methods[i].Nname = meth } ntyp.Methods().Set(methods) if !ntyp.HasTParam() && !ntyp.HasShape() { // Generate all the methods for a new fully-instantiated type. g.instTypeList = append(g.instTypeList, ntyp) } } func (g *irgen) signature(recv *types.Field, sig *types2.Signature) *types.Type { tparams2 := sig.TParams() tparams := make([]*types.Field, tparams2.Len()) for i := range tparams { tp := tparams2.At(i).Obj() tparams[i] = types.NewField(g.pos(tp), g.sym(tp), g.typ1(tp.Type())) } do := func(typ *types2.Tuple) []*types.Field { fields := make([]*types.Field, typ.Len()) for i := range fields { fields[i] = g.param(typ.At(i)) } return fields } params := do(sig.Params()) results := do(sig.Results()) if sig.Variadic() { params[len(params)-1].SetIsDDD(true) } return types.NewSignature(g.tpkg(sig), recv, tparams, params, results) } func (g *irgen) param(v *types2.Var) *types.Field { return types.NewField(g.pos(v), g.sym(v), g.typ1(v.Type())) } func (g *irgen) sym(obj types2.Object) *types.Sym { if name := obj.Name(); name != "" { return g.pkg(obj.Pkg()).Lookup(obj.Name()) } return nil } func (g *irgen) selector(obj types2.Object) *types.Sym { pkg, name := g.pkg(obj.Pkg()), obj.Name() if types.IsExported(name) { pkg = types.LocalPkg } return pkg.Lookup(name) } // tpkg returns the package that a function, interface, struct, or typeparam type // expression appeared in. // // Caveat: For the degenerate types "func()", "interface{}", and // "struct{}", tpkg always returns LocalPkg. However, we only need the // package information so that go/types can report it via its API, and // the reason we fail to return the original package for these // particular types is because go/types does *not* report it for // them. So in practice this limitation is probably moot. func (g *irgen) tpkg(typ types2.Type) *types.Pkg { if obj := anyObj(typ); obj != nil { return g.pkg(obj.Pkg()) } return types.LocalPkg } // anyObj returns some object accessible from typ, if any. func anyObj(typ types2.Type) types2.Object { switch typ := typ.(type) { case *types2.Signature: if recv := typ.Recv(); recv != nil { return recv } if params := typ.Params(); params.Len() > 0 { return params.At(0) } if results := typ.Results(); results.Len() > 0 { return results.At(0) } case *types2.Struct: if typ.NumFields() > 0 { return typ.Field(0) } case *types2.Interface: if typ.NumExplicitMethods() > 0 { return typ.ExplicitMethod(0) } case *types2.TypeParam: return typ.Obj() } return nil } func (g *irgen) basic(typ *types2.Basic) *types.Type { switch typ.Name() { case "byte": return types.ByteType case "rune": return types.RuneType } return *basics[typ.Kind()] } var basics = [...]**types.Type{ types2.Invalid: new(*types.Type), types2.Bool: &types.Types[types.TBOOL], types2.Int: &types.Types[types.TINT], types2.Int8: &types.Types[types.TINT8], types2.Int16: &types.Types[types.TINT16], types2.Int32: &types.Types[types.TINT32], types2.Int64: &types.Types[types.TINT64], types2.Uint: &types.Types[types.TUINT], types2.Uint8: &types.Types[types.TUINT8], types2.Uint16: &types.Types[types.TUINT16], types2.Uint32: &types.Types[types.TUINT32], types2.Uint64: &types.Types[types.TUINT64], types2.Uintptr: &types.Types[types.TUINTPTR], types2.Float32: &types.Types[types.TFLOAT32], types2.Float64: &types.Types[types.TFLOAT64], types2.Complex64: &types.Types[types.TCOMPLEX64], types2.Complex128: &types.Types[types.TCOMPLEX128], types2.String: &types.Types[types.TSTRING], types2.UnsafePointer: &types.Types[types.TUNSAFEPTR], types2.UntypedBool: &types.UntypedBool, types2.UntypedInt: &types.UntypedInt, types2.UntypedRune: &types.UntypedRune, types2.UntypedFloat: &types.UntypedFloat, types2.UntypedComplex: &types.UntypedComplex, types2.UntypedString: &types.UntypedString, types2.UntypedNil: &types.Types[types.TNIL], } var dirs = [...]types.ChanDir{ types2.SendRecv: types.Cboth, types2.SendOnly: types.Csend, types2.RecvOnly: types.Crecv, } // deref2 does a single deref of types2 type t, if it is a pointer type. func deref2(t types2.Type) types2.Type { if ptr := types2.AsPointer(t); ptr != nil { t = ptr.Elem() } return t }