Newer
Older
fun := sel.Obj().(*types2.Func)
sig := fun.Type().(*types2.Signature)
w.typ(recv)
w.typ(sig)
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
w.pos(expr)
w.selector(fun)
// Method on a type parameter. These require an indirect call
// through the current function's runtime dictionary.
if typeParam, ok := recv.(*types2.TypeParam); w.Bool(ok) {
typeParamIdx := w.dict.typeParamIndex(typeParam)
methodInfo := w.p.selectorIdx(fun)
w.Len(w.dict.typeParamMethodExprIdx(typeParamIdx, methodInfo))
return
}
if isInterface(recv) != isInterface(sig.Recv().Type()) {
w.p.fatalf(expr, "isInterface inconsistency: %v and %v", recv, sig.Recv().Type())
}
if !isInterface(recv) {
if named, ok := deref2(recv).(*types2.Named); ok {
obj, targs := splitNamed(named)
info := w.p.objInstIdx(obj, targs, w.dict)
// Method on a derived receiver type. These can be handled by a
// static call to the shaped method, but require dynamically
// looking up the appropriate dictionary argument in the current
// function's runtime dictionary.
if w.p.hasImplicitTypeParams(obj) || info.anyDerived() {
w.Bool(true) // dynamic subdictionary
w.Len(w.dict.subdictIdx(info))
return
}
// Method on a fully known receiver type. These can be handled
// by a static call to the shaped method, and with a static
// reference to the receiver type's dictionary.
if targs.Len() != 0 {
w.Bool(false) // no dynamic subdictionary
w.Bool(true) // static dictionary
w.objInfo(info)
return
}
}
}
w.Bool(false) // no dynamic subdictionary
w.Bool(false) // no static dictionary
}
Matthew Dempsky
committed
// multiExpr writes a sequence of expressions, where the i'th value is
// implicitly converted to dstType(i). It also handles when exprs is a
// single, multi-valued expression (e.g., the multi-valued argument in
// an f(g()) call, or the RHS operand in a comma-ok assignment).
func (w *writer) multiExpr(pos poser, dstType func(int) types2.Type, exprs []syntax.Expr) {
w.Sync(pkgbits.SyncMultiExpr)
if len(exprs) == 1 {
expr := exprs[0]
if tuple, ok := w.p.typeOf(expr).(*types2.Tuple); ok {
assert(tuple.Len() > 1)
Matthew Dempsky
committed
w.Bool(true) // N:1 assignment
w.pos(pos)
w.expr(expr)
w.Len(tuple.Len())
for i := 0; i < tuple.Len(); i++ {
src := tuple.At(i).Type()
// TODO(mdempsky): Investigate not writing src here. I think
// the reader should be able to infer it from expr anyway.
w.typ(src)
if dst := dstType(i); w.Bool(dst != nil && !types2.Identical(src, dst)) {
if src == nil || dst == nil {
w.p.fatalf(pos, "src is %v, dst is %v", src, dst)
}
if !types2.AssignableTo(src, dst) {
w.p.fatalf(pos, "%v is not assignable to %v", src, dst)
}
w.typ(dst)
w.convRTTI(src, dst)
Matthew Dempsky
committed
}
}
Matthew Dempsky
committed
return
}
}
Matthew Dempsky
committed
w.Bool(false) // N:N assignment
w.Len(len(exprs))
Matthew Dempsky
committed
for i, expr := range exprs {
w.implicitConvExpr(dstType(i), expr)
Matthew Dempsky
committed
}
}
// implicitConvExpr is like expr, but if dst is non-nil and different
// from expr's type, then an implicit conversion operation is inserted
// at expr's position.
func (w *writer) implicitConvExpr(dst types2.Type, expr syntax.Expr) {
w.convertExpr(dst, expr, true)
}
func (w *writer) convertExpr(dst types2.Type, expr syntax.Expr, implicit bool) {
Matthew Dempsky
committed
src := w.p.typeOf(expr)
// Omit implicit no-op conversions.
identical := dst == nil || types2.Identical(src, dst)
if implicit && identical {
w.expr(expr)
return
Matthew Dempsky
committed
}
if implicit && !types2.AssignableTo(src, dst) {
w.p.fatalf(expr, "%v is not assignable to %v", src, dst)
}
w.Code(exprConvert)
w.Bool(implicit)
w.typ(dst)
w.pos(expr)
w.convRTTI(src, dst)
w.Bool(isTypeParam(dst))
Matthew Dempsky
committed
w.Bool(identical)
Matthew Dempsky
committed
w.expr(expr)
}
func (w *writer) compLit(lit *syntax.CompositeLit) {
typ := w.p.typeOf(lit)
w.Sync(pkgbits.SyncCompLit)
w.typ(typ)
if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok {
typ = ptr.Elem()
}
Matthew Dempsky
committed
var keyType, elemType types2.Type
var structType *types2.Struct
switch typ0 := typ; typ := types2.CoreType(typ).(type) {
Matthew Dempsky
committed
default:
w.p.fatalf(lit, "unexpected composite literal type: %v", typ)
case *types2.Array:
elemType = typ.Elem()
case *types2.Map:
w.rtype(typ0)
Matthew Dempsky
committed
keyType, elemType = typ.Key(), typ.Elem()
case *types2.Slice:
elemType = typ.Elem()
case *types2.Struct:
structType = typ
}
w.Len(len(lit.ElemList))
for i, elem := range lit.ElemList {
Matthew Dempsky
committed
elemType := elemType
if structType != nil {
if kv, ok := elem.(*syntax.KeyValueExpr); ok {
// use position of expr.Key rather than of elem (which has position of ':')
w.pos(kv.Key)
Matthew Dempsky
committed
i = fieldIndex(w.p.info, structType, kv.Key.(*syntax.Name))
elem = kv.Value
} else {
w.pos(elem)
}
Matthew Dempsky
committed
elemType = structType.Field(i).Type()
w.Len(i)
if kv, ok := elem.(*syntax.KeyValueExpr); w.Bool(ok) {
// use position of expr.Key rather than of elem (which has position of ':')
w.pos(kv.Key)
w.implicitConvExpr(keyType, kv.Key)
elem = kv.Value
}
}
w.pos(elem)
w.implicitConvExpr(elemType, elem)
}
}
func (w *writer) funcLit(expr *syntax.FuncLit) {
sig := w.p.typeOf(expr).(*types2.Signature)
body, closureVars := w.p.bodyIdx(sig, expr.Body, w.dict)
w.Sync(pkgbits.SyncFuncLit)
w.pos(expr)
w.signature(sig)
w.Len(len(closureVars))
for _, cv := range closureVars {
w.pos(cv.pos)
w.useLocal(cv.pos, cv.var_)
w.Reloc(pkgbits.RelocBody, body)
type posVar struct {
pos syntax.Pos
var_ *types2.Var
}
func (w *writer) exprList(expr syntax.Expr) {
w.Sync(pkgbits.SyncExprList)
w.exprs(unpackListExpr(expr))
}
func (w *writer) exprs(exprs []syntax.Expr) {
w.Sync(pkgbits.SyncExprs)
w.Len(len(exprs))
for _, expr := range exprs {
w.expr(expr)
}
}
// rtype writes information so that the reader can construct an
// expression of type *runtime._type representing typ.
func (w *writer) rtype(typ types2.Type) {
typ = types2.Default(typ)
info := w.p.typIdx(typ, w.dict)
w.rtypeInfo(info)
}
func (w *writer) rtypeInfo(info typeInfo) {
w.Sync(pkgbits.SyncRType)
if w.Bool(info.derived) {
w.Len(w.dict.rtypeIdx(info))
} else {
w.typInfo(info)
}
}
// varDictIndex writes out information for populating DictIndex for
// the ir.Name that will represent obj.
func (w *writer) varDictIndex(obj *types2.Var) {
info := w.p.typIdx(obj.Type(), w.dict)
if w.Bool(info.derived) {
w.Len(w.dict.rtypeIdx(info))
}
}
func isUntyped(typ types2.Type) bool {
basic, ok := typ.(*types2.Basic)
return ok && basic.Info()&types2.IsUntyped != 0
}
func isTuple(typ types2.Type) bool {
_, ok := typ.(*types2.Tuple)
return ok
}
func (w *writer) itab(typ, iface types2.Type) {
typ = types2.Default(typ)
iface = types2.Default(iface)
typInfo := w.p.typIdx(typ, w.dict)
ifaceInfo := w.p.typIdx(iface, w.dict)
w.rtypeInfo(typInfo)
w.rtypeInfo(ifaceInfo)
if w.Bool(typInfo.derived || ifaceInfo.derived) {
w.Len(w.dict.itabIdx(typInfo, ifaceInfo))
}
// convRTTI writes information so that the reader can construct
// expressions for converting from src to dst.
func (w *writer) convRTTI(src, dst types2.Type) {
w.Sync(pkgbits.SyncConvRTTI)
w.itab(src, dst)
}
func (w *writer) exprType(iface types2.Type, typ syntax.Expr) {
base.Assertf(iface == nil || isInterface(iface), "%v must be nil or an interface type", iface)
tv := w.p.typeAndValue(typ)
assert(tv.IsType())
w.Sync(pkgbits.SyncExprType)
w.pos(typ)
if w.Bool(iface != nil && !iface.Underlying().(*types2.Interface).Empty()) {
w.itab(tv.Type, iface)
} else {
w.rtype(tv.Type)
info := w.p.typIdx(tv.Type, w.dict)
w.Bool(info.derived)
}
}
// isInterface reports whether typ is known to be an interface type.
// If typ is a type parameter, then isInterface reports an internal
// compiler error instead.
func isInterface(typ types2.Type) bool {
if _, ok := typ.(*types2.TypeParam); ok {
// typ is a type parameter and may be instantiated as either a
// concrete or interface type, so the writer can't depend on
// knowing this.
base.Fatalf("%v is a type parameter", typ)
}
_, ok := typ.Underlying().(*types2.Interface)
return ok
}
// op writes an Op into the bitstream.
func (w *writer) op(op ir.Op) {
// TODO(mdempsky): Remove in favor of explicit codes? Would make
// export data more stable against internal refactorings, but low
// priority at the moment.
assert(op != 0)
w.Sync(pkgbits.SyncOp)
w.Len(int(op))
}
// @@@ Package initialization
// Caution: This code is still clumsy, because toolstash -cmp is
// particularly sensitive to it.
type typeDeclGen struct {
*syntax.TypeDecl
gen int
// Implicit type parameters in scope at this type declaration.
implicits []*types2.TypeName
type fileImports struct {
importedEmbed, importedUnsafe bool
}
// declCollector is a visitor type that collects compiler-needed
// information about declarations that types2 doesn't track.
//
// Notably, it maps declared types and functions back to their
// declaration statement, keeps track of implicit type parameters, and
// assigns unique type "generation" numbers to local defined types.
type declCollector struct {
pw *pkgWriter
typegen *int
file *fileImports
withinFunc bool
implicits []*types2.TypeName
}
func (c *declCollector) withTParams(obj types2.Object) *declCollector {
tparams := objTypeParams(obj)
Robert Griesemer
committed
n := tparams.Len()
if n == 0 {
return c
}
copy := *c
copy.implicits = copy.implicits[:len(copy.implicits):len(copy.implicits)]
Robert Griesemer
committed
for i := 0; i < n; i++ {
Robert Griesemer
committed
copy.implicits = append(copy.implicits, tparams.At(i).Obj())
Robert Griesemer
committed
}
return ©
func (c *declCollector) Visit(n syntax.Node) syntax.Visitor {
pw := c.pw
switch n := n.(type) {
case *syntax.File:
pw.checkPragmas(n.Pragma, ir.GoBuildPragma, false)
case *syntax.ImportDecl:
pw.checkPragmas(n.Pragma, 0, false)
switch pkgNameOf(pw.info, n).Imported().Path() {
case "embed":
c.file.importedEmbed = true
case "unsafe":
c.file.importedUnsafe = true
}
case *syntax.ConstDecl:
pw.checkPragmas(n.Pragma, 0, false)
case *syntax.FuncDecl:
pw.checkPragmas(n.Pragma, funcPragmas, false)
obj := pw.info.Defs[n.Name].(*types2.Func)
pw.funDecls[obj] = n
return c.withTParams(obj)
case *syntax.TypeDecl:
obj := pw.info.Defs[n.Name].(*types2.TypeName)
d := typeDeclGen{TypeDecl: n, implicits: c.implicits}
if n.Alias {
pw.checkPragmas(n.Pragma, 0, false)
} else {
pw.checkPragmas(n.Pragma, 0, false)
// Assign a unique ID to function-scoped defined types.
if c.withinFunc {
*c.typegen++
d.gen = *c.typegen
}
}
pw.typDecls[obj] = d
// TODO(mdempsky): Omit? Not strictly necessary; only matters for
// type declarations within function literals within parameterized
// type declarations, but types2 the function literals will be
// constant folded away.
return c.withTParams(obj)
case *syntax.VarDecl:
pw.checkPragmas(n.Pragma, 0, true)
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
if p, ok := n.Pragma.(*pragmas); ok && len(p.Embeds) > 0 {
if err := checkEmbed(n, c.file.importedEmbed, c.withinFunc); err != nil {
pw.errorf(p.Embeds[0].Pos, "%s", err)
}
}
case *syntax.BlockStmt:
if !c.withinFunc {
copy := *c
copy.withinFunc = true
return ©
}
}
return c
}
func (pw *pkgWriter) collectDecls(noders []*noder) {
var typegen int
for _, p := range noders {
var file fileImports
syntax.Walk(p.file, &declCollector{
pw: pw,
typegen: &typegen,
file: &file,
})
pw.cgoPragmas = append(pw.cgoPragmas, p.pragcgobuf...)
for _, l := range p.linknames {
if !file.importedUnsafe {
pw.errorf(l.pos, "//go:linkname only allowed in Go files that import \"unsafe\"")
continue
}
switch obj := pw.curpkg.Scope().Lookup(l.local).(type) {
case *types2.Func, *types2.Var:
if _, ok := pw.linknames[obj]; !ok {
pw.linknames[obj] = l.remote
} else {
pw.errorf(l.pos, "duplicate //go:linkname for %s", l.local)
}
default:
if types.AllowsGoVersion(1, 18) {
pw.errorf(l.pos, "//go:linkname must refer to declared function or variable")
}
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
}
}
}
}
func (pw *pkgWriter) checkPragmas(p syntax.Pragma, allowed ir.PragmaFlag, embedOK bool) {
if p == nil {
return
}
pragma := p.(*pragmas)
for _, pos := range pragma.Pos {
if pos.Flag&^allowed != 0 {
pw.errorf(pos.Pos, "misplaced compiler directive")
}
}
if !embedOK {
for _, e := range pragma.Embeds {
pw.errorf(e.Pos, "misplaced go:embed directive")
}
}
}
func (w *writer) pkgInit(noders []*noder) {
w.Len(len(w.p.cgoPragmas))
for _, cgoPragma := range w.p.cgoPragmas {
w.Strings(cgoPragma)
w.Sync(pkgbits.SyncDecls)
for _, p := range noders {
for _, decl := range p.file.DeclList {
w.pkgDecl(decl)
}
}
w.Code(declEnd)
w.Sync(pkgbits.SyncEOF)
}
func (w *writer) pkgDecl(decl syntax.Decl) {
switch decl := decl.(type) {
default:
w.p.unexpected("declaration", decl)
case *syntax.ImportDecl:
case *syntax.ConstDecl:
w.Code(declOther)
w.pkgObjs(decl.NameList...)
case *syntax.FuncDecl:
if decl.Name.Value == "_" {
break // skip blank functions
}
obj := w.p.info.Defs[decl.Name].(*types2.Func)
sig := obj.Type().(*types2.Signature)
if sig.RecvTypeParams() != nil || sig.TypeParams() != nil {
break // skip generic functions
}
if recv := sig.Recv(); recv != nil {
w.Code(declMethod)
w.typ(recvBase(recv))
w.selector(obj)
break
}
w.Code(declFunc)
w.pkgObjs(decl.Name)
case *syntax.TypeDecl:
if len(decl.TParamList) != 0 {
break // skip generic type decls
}
if decl.Name.Value == "_" {
break // skip blank type decls
}
name := w.p.info.Defs[decl.Name].(*types2.TypeName)
// Skip type declarations for interfaces that are only usable as
// type parameter bounds.
Robert Griesemer
committed
if iface, ok := name.Type().Underlying().(*types2.Interface); ok && !iface.IsMethodSet() {
break
}
w.Code(declOther)
w.pkgObjs(decl.Name)
case *syntax.VarDecl:
w.Code(declVar)
w.pos(decl)
w.pkgObjs(decl.NameList...)
Matthew Dempsky
committed
// TODO(mdempsky): It would make sense to use multiExpr here, but
// that results in IR that confuses pkginit/initorder.go. So we
// continue using exprList, and let typecheck handle inserting any
// implicit conversions. That's okay though, because package-scope
// assignments never require dictionaries.
w.exprList(decl.Values)
var embeds []pragmaEmbed
if p, ok := decl.Pragma.(*pragmas); ok {
embeds = p.Embeds
}
w.Len(len(embeds))
for _, embed := range embeds {
w.pos(embed.Pos)
w.Strings(embed.Patterns)
}
}
}
func (w *writer) pkgObjs(names ...*syntax.Name) {
w.Sync(pkgbits.SyncDeclNames)
w.Len(len(names))
for _, name := range names {
obj, ok := w.p.info.Defs[name]
assert(ok)
w.Sync(pkgbits.SyncDeclName)
w.obj(obj, nil)
}
}
// @@@ Helpers
// hasImplicitTypeParams reports whether obj is a defined type with
// implicit type parameters (e.g., declared within a generic function
// or method).
func (p *pkgWriter) hasImplicitTypeParams(obj *types2.TypeName) bool {
if obj.Pkg() == p.curpkg {
decl, ok := p.typDecls[obj]
assert(ok)
if len(decl.implicits) != 0 {
return true
}
}
return false
}
// isDefinedType reports whether obj is a defined type.
func isDefinedType(obj types2.Object) bool {
if obj, ok := obj.(*types2.TypeName); ok {
return !obj.IsAlias()
}
return false
}
// isGlobal reports whether obj was declared at package scope.
//
// Caveat: blank objects are not declared.
func isGlobal(obj types2.Object) bool {
return obj.Parent() == obj.Pkg().Scope()
}
// lookupObj returns the object that expr refers to, if any. If expr
Robert Griesemer
committed
// is an explicit instantiation of a generic object, then the instance
// object is returned as well.
func lookupObj(p *pkgWriter, expr syntax.Expr) (obj types2.Object, inst types2.Instance) {
if index, ok := expr.(*syntax.IndexExpr); ok {
Robert Griesemer
committed
args := unpackListExpr(index.Index)
if len(args) == 1 {
tv := p.typeAndValue(args[0])
Robert Griesemer
committed
if tv.IsValue() {
return // normal index expression
}
}
expr = index.X
}
// Strip package qualifier, if present.
if sel, ok := expr.(*syntax.SelectorExpr); ok {
if !isPkgQual(p.info, sel) {
return // normal selector expression
}
expr = sel.Sel
}
if name, ok := expr.(*syntax.Name); ok {
obj = p.info.Uses[name]
inst = p.info.Instances[name]
}
return
}
// isPkgQual reports whether the given selector expression is a
// package-qualified identifier.
func isPkgQual(info *types2.Info, sel *syntax.SelectorExpr) bool {
if name, ok := sel.X.(*syntax.Name); ok {
_, isPkgName := info.Uses[name].(*types2.PkgName)
return isPkgName
}
return false
}
// isNil reports whether expr is a (possibly parenthesized) reference
// to the predeclared nil value.
func isNil(p *pkgWriter, expr syntax.Expr) bool {
tv := p.typeAndValue(expr)
return tv.IsNil()
}
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
// recvBase returns the base type for the given receiver parameter.
func recvBase(recv *types2.Var) *types2.Named {
typ := recv.Type()
if ptr, ok := typ.(*types2.Pointer); ok {
typ = ptr.Elem()
}
return typ.(*types2.Named)
}
// namesAsExpr returns a list of names as a syntax.Expr.
func namesAsExpr(names []*syntax.Name) syntax.Expr {
if len(names) == 1 {
return names[0]
}
exprs := make([]syntax.Expr, len(names))
for i, name := range names {
exprs[i] = name
}
return &syntax.ListExpr{ElemList: exprs}
}
// fieldIndex returns the index of the struct field named by key.
func fieldIndex(info *types2.Info, str *types2.Struct, key *syntax.Name) int {
field := info.Uses[key].(*types2.Var)
for i := 0; i < str.NumFields(); i++ {
if str.Field(i) == field {
return i
}
}
panic(fmt.Sprintf("%s: %v is not a field of %v", key.Pos(), field, str))
}
// objTypeParams returns the type parameters on the given object.
func objTypeParams(obj types2.Object) *types2.TypeParamList {
switch obj := obj.(type) {
case *types2.Func:
sig := obj.Type().(*types2.Signature)
if sig.Recv() != nil {
return sig.RecvTypeParams()
}
return sig.TypeParams()
case *types2.TypeName:
if !obj.IsAlias() {
return obj.Type().(*types2.Named).TypeParams()
}
}
return nil
}
// splitNamed decomposes a use of a defined type into its original
// type definition and the type arguments used to instantiate it.
func splitNamed(typ *types2.Named) (*types2.TypeName, *types2.TypeList) {
base.Assertf(typ.TypeParams().Len() == typ.TypeArgs().Len(), "use of uninstantiated type: %v", typ)
orig := typ.Origin()
base.Assertf(orig.TypeArgs() == nil, "origin %v of %v has type arguments", orig, typ)
base.Assertf(typ.Obj() == orig.Obj(), "%v has object %v, but %v has object %v", typ, typ.Obj(), orig, orig.Obj())
return typ.Obj(), typ.TypeArgs()
}
func asPragmaFlag(p syntax.Pragma) ir.PragmaFlag {
if p == nil {
return 0
}
return p.(*pragmas).Flag
}
func asWasmImport(p syntax.Pragma) *WasmImport {
if p == nil {
return nil
}
return p.(*pragmas).WasmImport
}
// isPtrTo reports whether from is the type *to.
func isPtrTo(from, to types2.Type) bool {
ptr, ok := from.(*types2.Pointer)
return ok && types2.Identical(ptr.Elem(), to)
}