Skip to content
Snippets Groups Projects
writer.go 68.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	fun := sel.Obj().(*types2.Func)
    	sig := fun.Type().(*types2.Signature)
    
    	w.typ(recv)
    
    	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
    }
    
    
    // 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)
    
    			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.implicitConvExpr(dstType(i), expr)
    
    // 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) {
    
    
    	// Omit implicit no-op conversions.
    	identical := dst == nil || types2.Identical(src, dst)
    	if implicit && identical {
    		w.expr(expr)
    		return
    
    
    	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))
    
    func (w *writer) compLit(lit *syntax.CompositeLit) {
    
    	w.Sync(pkgbits.SyncCompLit)
    
    	if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok {
    
    	var keyType, elemType types2.Type
    	var structType *types2.Struct
    
    	switch typ0 := typ; typ := types2.CoreType(typ).(type) {
    
    	default:
    		w.p.fatalf(lit, "unexpected composite literal type: %v", typ)
    	case *types2.Array:
    		elemType = typ.Elem()
    	case *types2.Map:
    
    		keyType, elemType = typ.Key(), typ.Elem()
    	case *types2.Slice:
    		elemType = typ.Elem()
    	case *types2.Struct:
    		structType = typ
    	}
    
    	for i, elem := range lit.ElemList {
    
    			if kv, ok := elem.(*syntax.KeyValueExpr); ok {
    				// use position of expr.Key rather than of elem (which has position of ':')
    				w.pos(kv.Key)
    
    				i = fieldIndex(w.p.info, structType, kv.Key.(*syntax.Name))
    
    			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)
    
    		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)
    
    	for _, cv := range closureVars {
    		w.pos(cv.pos)
    
    	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) {
    
    	info := w.p.typIdx(typ, w.dict)
    	w.rtypeInfo(info)
    }
    
    func (w *writer) rtypeInfo(info typeInfo) {
    
    	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)
    
    	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)
    
    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)
    
    	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)
    
    		return c
    	}
    
    	copy := *c
    	copy.implicits = copy.implicits[:len(copy.implicits):len(copy.implicits)]
    
    		copy.implicits = append(copy.implicits, tparams.At(i).Obj())
    
    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
    
    	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.
    
    		// 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)
    
    		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 &copy
    		}
    	}
    
    	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 {
    
    				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")
    				}
    
    			}
    		}
    	}
    }
    
    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 {
    
    	for _, p := range noders {
    		for _, decl := range p.file.DeclList {
    			w.pkgDecl(decl)
    		}
    	}
    
    }
    
    func (w *writer) pkgDecl(decl syntax.Decl) {
    	switch decl := decl.(type) {
    	default:
    		w.p.unexpected("declaration", decl)
    
    	case *syntax.ImportDecl:
    
    	case *syntax.ConstDecl:
    
    		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 {
    
    			w.typ(recvBase(recv))
    			w.selector(obj)
    			break
    		}
    
    
    		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.
    
    		if iface, ok := name.Type().Underlying().(*types2.Interface); ok && !iface.IsMethodSet() {
    
    		w.pkgObjs(decl.Name)
    
    	case *syntax.VarDecl:
    
    		w.pos(decl)
    		w.pkgObjs(decl.NameList...)
    
    
    		// 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
    		}
    
    		for _, embed := range embeds {
    			w.pos(embed.Pos)
    
    		}
    	}
    }
    
    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)
    
    // 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
    
    // 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 {
    
    		args := unpackListExpr(index.Index)
    		if len(args) == 1 {
    
    			tv := p.typeAndValue(args[0])
    
    			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)
    
    // 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()
    
    	case *types2.TypeName:
    		if !obj.IsAlias() {
    
    			return obj.Type().(*types2.Named).TypeParams()
    
    // 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)
    }