Skip to content
Snippets Groups Projects
writer.go 48.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	if idx, ok := w.localsIdx[obj]; w.Bool(ok) {
    		w.Len(idx)
    
    		return
    	}
    
    	idx, ok := w.closureVarsIdx[obj]
    	if !ok {
    		if w.closureVarsIdx == nil {
    			w.closureVarsIdx = make(map[*types2.Var]int)
    		}
    		idx = len(w.closureVars)
    
    		w.closureVars = append(w.closureVars, posVar{pos, obj})
    
    }
    
    func (w *writer) openScope(pos syntax.Pos) {
    
    	w.Sync(pkgbits.SyncOpenScope)
    
    	w.pos(pos)
    }
    
    func (w *writer) closeScope(pos syntax.Pos) {
    
    	w.Sync(pkgbits.SyncCloseScope)
    
    	w.pos(pos)
    	w.closeAnotherScope()
    }
    
    func (w *writer) closeAnotherScope() {
    
    	w.Sync(pkgbits.SyncCloseAnotherScope)
    
    // stmt writes the given statement into the function body bitstream.
    
    func (w *writer) stmt(stmt syntax.Stmt) {
    	var stmts []syntax.Stmt
    	if stmt != nil {
    		stmts = []syntax.Stmt{stmt}
    	}
    	w.stmts(stmts)
    }
    
    func (w *writer) stmts(stmts []syntax.Stmt) {
    
    	for _, stmt := range stmts {
    		w.stmt1(stmt)
    	}
    
    	w.Code(stmtEnd)
    	w.Sync(pkgbits.SyncStmtsEnd)
    
    }
    
    func (w *writer) stmt1(stmt syntax.Stmt) {
    	switch stmt := stmt.(type) {
    	default:
    		w.p.unexpected("statement", stmt)
    
    	case nil, *syntax.EmptyStmt:
    		return
    
    	case *syntax.AssignStmt:
    		switch {
    		case stmt.Rhs == nil:
    
    			w.op(binOps[stmt.Op])
    			w.expr(stmt.Lhs)
    			w.pos(stmt)
    
    		case stmt.Op != 0 && stmt.Op != syntax.Def:
    
    			w.op(binOps[stmt.Op])
    			w.expr(stmt.Lhs)
    			w.pos(stmt)
    			w.expr(stmt.Rhs)
    
    		default:
    
    		w.blockStmt(stmt)
    
    	case *syntax.BranchStmt:
    
    		w.pos(stmt)
    		w.op(branchOps[stmt.Tok])
    		w.optLabel(stmt.Label)
    
    	case *syntax.CallStmt:
    
    		w.pos(stmt)
    		w.op(callOps[stmt.Tok])
    		w.expr(stmt.Call)
    
    	case *syntax.DeclStmt:
    		for _, decl := range stmt.DeclList {
    			w.declStmt(decl)
    		}
    
    	case *syntax.ExprStmt:
    
    		w.expr(stmt.X)
    
    	case *syntax.ForStmt:
    
    		w.forStmt(stmt)
    
    	case *syntax.IfStmt:
    
    		w.ifStmt(stmt)
    
    	case *syntax.LabeledStmt:
    
    		w.pos(stmt)
    		w.label(stmt.Label)
    		w.stmt1(stmt.Stmt)
    
    	case *syntax.ReturnStmt:
    
    		w.pos(stmt)
    		w.exprList(stmt.Results)
    
    	case *syntax.SelectStmt:
    
    		w.selectStmt(stmt)
    
    	case *syntax.SendStmt:
    
    		w.pos(stmt)
    		w.expr(stmt.Chan)
    		w.expr(stmt.Value)
    
    	case *syntax.SwitchStmt:
    
    		w.switchStmt(stmt)
    	}
    }
    
    func (w *writer) assignList(expr syntax.Expr) {
    	exprs := unpackListExpr(expr)
    
    		w.assign(expr)
    	}
    }
    
    func (w *writer) assign(expr syntax.Expr) {
    	expr = unparen(expr)
    
    	if name, ok := expr.(*syntax.Name); ok {
    		if name.Value == "_" {
    			w.Code(assignBlank)
    			return
    
    		if obj, ok := w.p.info.Defs[name]; ok {
    			obj := obj.(*types2.Var)
    
    			w.Code(assignDef)
    			w.pos(obj)
    			w.localIdent(obj)
    			w.typ(obj.Type())
    
    			// TODO(mdempsky): Minimize locals index size by deferring
    			// this until the variables actually come into scope.
    			w.addLocal(obj)
    			return
    		}
    
    }
    
    func (w *writer) declStmt(decl syntax.Decl) {
    	switch decl := decl.(type) {
    	default:
    		w.p.unexpected("declaration", decl)
    
    
    	case *syntax.ConstDecl, *syntax.TypeDecl:
    
    		w.assignList(namesAsExpr(decl.NameList))
    
    	}
    }
    
    func (w *writer) blockStmt(stmt *syntax.BlockStmt) {
    
    	w.Sync(pkgbits.SyncBlockStmt)
    
    	w.openScope(stmt.Pos())
    	w.stmts(stmt.List)
    	w.closeScope(stmt.Rbrace)
    }
    
    func (w *writer) forStmt(stmt *syntax.ForStmt) {
    
    	w.Sync(pkgbits.SyncForStmt)
    
    	if rang, ok := stmt.Init.(*syntax.RangeClause); w.Bool(ok) {
    
    	} else {
    		w.pos(stmt)
    		w.stmt(stmt.Init)
    
    		w.stmt(stmt.Post)
    	}
    
    	w.blockStmt(stmt.Body)
    	w.closeAnotherScope()
    }
    
    func (w *writer) ifStmt(stmt *syntax.IfStmt) {
    
    	w.Sync(pkgbits.SyncIfStmt)
    
    	w.openScope(stmt.Pos())
    	w.pos(stmt)
    	w.stmt(stmt.Init)
    	w.expr(stmt.Cond)
    	w.blockStmt(stmt.Then)
    	w.stmt(stmt.Else)
    	w.closeAnotherScope()
    }
    
    func (w *writer) selectStmt(stmt *syntax.SelectStmt) {
    
    	w.Sync(pkgbits.SyncSelectStmt)
    
    	for i, clause := range stmt.Body {
    		if i > 0 {
    			w.closeScope(clause.Pos())
    		}
    		w.openScope(clause.Pos())
    
    		w.pos(clause)
    		w.stmt(clause.Comm)
    		w.stmts(clause.Body)
    	}
    	if len(stmt.Body) > 0 {
    		w.closeScope(stmt.Rbrace)
    	}
    }
    
    func (w *writer) switchStmt(stmt *syntax.SwitchStmt) {
    
    	w.Sync(pkgbits.SyncSwitchStmt)
    
    
    	w.openScope(stmt.Pos())
    	w.pos(stmt)
    	w.stmt(stmt.Init)
    
    	if guard, ok := stmt.Tag.(*syntax.TypeSwitchGuard); w.Bool(ok) {
    
    		tv, ok := w.p.info.Types[guard.X]
    		assert(ok && tv.IsValue())
    		iface = tv.Type
    
    
    		if tag := guard.Lhs; w.Bool(tag != nil) {
    
    	for i, clause := range stmt.Body {
    		if i > 0 {
    			w.closeScope(clause.Pos())
    		}
    		w.openScope(clause.Pos())
    
    		w.pos(clause)
    
    
    		if iface != nil {
    			cases := unpackListExpr(clause.Cases)
    			w.Len(len(cases))
    			for _, cas := range cases {
    				w.exprType(iface, cas, true)
    			}
    		} else {
    			w.exprList(clause.Cases)
    		}
    
    
    		if obj, ok := w.p.info.Implicits[clause]; ok {
    			// TODO(mdempsky): These pos details are quirkish, but also
    			// necessary so the variable's position is correct for DWARF
    			// scope assignment later. It would probably be better for us to
    			// instead just set the variable's DWARF scoping info earlier so
    			// we can give it the correct position information.
    			pos := clause.Pos()
    			if typs := unpackListExpr(clause.Cases); len(typs) != 0 {
    				pos = typeExprEndPos(typs[len(typs)-1])
    			}
    			w.pos(pos)
    
    			obj := obj.(*types2.Var)
    			w.typ(obj.Type())
    			w.addLocal(obj)
    		}
    
    		w.stmts(clause.Body)
    	}
    	if len(stmt.Body) > 0 {
    		w.closeScope(stmt.Rbrace)
    	}
    
    	w.closeScope(stmt.Rbrace)
    }
    
    func (w *writer) label(label *syntax.Name) {
    
    
    	// TODO(mdempsky): Replace label strings with dense indices.
    
    }
    
    func (w *writer) optLabel(label *syntax.Name) {
    
    	w.Sync(pkgbits.SyncOptLabel)
    	if w.Bool(label != nil) {
    
    // expr writes the given expression into the function body bitstream.
    
    func (w *writer) expr(expr syntax.Expr) {
    
    	base.Assertf(expr != nil, "missing expression")
    
    
    	expr = unparen(expr) // skip parens; unneeded after typecheck
    
    
    	obj, inst := lookupObj(w.p.info, expr)
    	targs := inst.TypeArgs
    
    
    	if tv, ok := w.p.info.Types[expr]; ok {
    
    		// TODO(mdempsky): Be more judicious about which types are marked as "needed".
    
    		if inst.Type != nil {
    			w.needType(inst.Type)
    		} else {
    			w.needType(tv.Type)
    		}
    
    			w.p.fatalf(expr, "unexpected type expression %v", syntax.String(expr))
    
    
    			// TODO(mdempsky): These details are only important for backend
    			// diagnostics. Explore writing them out separately.
    			w.op(constExprOp(expr))
    
    			w.String(syntax.String(expr))
    
    		return
    	}
    
    	switch expr := expr.(type) {
    	default:
    		w.p.unexpected("expression", expr)
    
    	case *syntax.CompositeLit:
    
    		w.compLit(expr)
    
    	case *syntax.FuncLit:
    
    		w.funcLit(expr)
    
    	case *syntax.SelectorExpr:
    		sel, ok := w.p.info.Selections[expr]
    		assert(ok)
    
    
    		if w.Bool(sel.Kind() == types2.MethodExpr) {
    			w.exprType(nil, expr.X, false)
    		} else {
    			w.expr(expr.X)
    		}
    
    		w.pos(expr)
    		w.selector(sel.Obj())
    
    	case *syntax.IndexExpr:
    		tv, ok := w.p.info.Types[expr.Index]
    		assert(ok && tv.IsValue())
    
    
    		w.expr(expr.X)
    		w.pos(expr)
    		w.expr(expr.Index)
    
    	case *syntax.SliceExpr:
    
    		w.expr(expr.X)
    		w.pos(expr)
    		for _, n := range &expr.Index {
    
    		tv, ok := w.p.info.Types[expr.X]
    		assert(ok && tv.IsValue())
    
    
    		w.exprType(tv.Type, expr.Type, false)
    
    
    	case *syntax.Operation:
    		if expr.Y == nil {
    
    			w.op(unOps[expr.Op])
    			w.pos(expr)
    			w.expr(expr.X)
    			break
    		}
    
    
    		w.op(binOps[expr.Op])
    		w.expr(expr.X)
    		w.pos(expr)
    		w.expr(expr.Y)
    
    	case *syntax.CallExpr:
    
    		tv, ok := w.p.info.Types[expr.Fun]
    		assert(ok)
    		if tv.IsType() {
    			assert(len(expr.ArgList) == 1)
    			assert(!expr.HasDots)
    
    
    		if name, ok := unparen(expr.Fun).(*syntax.Name); ok && tv.IsBuiltin() {
    			switch name.Value {
    			case "make":
    				assert(len(expr.ArgList) >= 1)
    				assert(!expr.HasDots)
    
    				w.Code(exprMake)
    				w.pos(expr)
    				w.exprType(nil, expr.ArgList[0], false)
    				w.exprs(expr.ArgList[1:])
    				return
    
    			case "new":
    				assert(len(expr.ArgList) == 1)
    				assert(!expr.HasDots)
    
    				w.Code(exprNew)
    				w.pos(expr)
    				w.exprType(nil, expr.ArgList[0], false)
    				return
    			}
    		}
    
    
    		writeFunExpr := func() {
    			if selector, ok := unparen(expr.Fun).(*syntax.SelectorExpr); ok {
    				if sel, ok := w.p.info.Selections[selector]; ok && sel.Kind() == types2.MethodVal {
    					w.expr(selector.X)
    
    					w.Bool(true) // method call
    
    			w.Bool(false) // not a method call (i.e., normal function call)
    
    		if w.Bool(len(expr.ArgList) == 1 && isMultiValueExpr(w.p.info, expr.ArgList[0])) {
    			// f(g()) call
    			assert(!expr.HasDots)
    			w.expr(expr.ArgList[0])
    		} else {
    			w.exprs(expr.ArgList)
    			w.Bool(expr.HasDots)
    		}
    
    func (w *writer) optExpr(expr syntax.Expr) {
    	if w.Bool(expr != nil) {
    		w.expr(expr)
    	}
    }
    
    
    func (w *writer) compLit(lit *syntax.CompositeLit) {
    	tv, ok := w.p.info.Types[lit]
    	assert(ok)
    
    
    	w.Sync(pkgbits.SyncCompLit)
    
    	w.pos(lit)
    	w.typ(tv.Type)
    
    	typ := tv.Type
    
    	if ptr, ok := types2.CoreType(typ).(*types2.Pointer); ok {
    
    	str, isStruct := types2.CoreType(typ).(*types2.Struct)
    
    	for i, elem := range lit.ElemList {
    		if isStruct {
    			if kv, ok := elem.(*syntax.KeyValueExpr); ok {
    				// use position of expr.Key rather than of elem (which has position of ':')
    				w.pos(kv.Key)
    
    				w.Len(fieldIndex(w.p.info, str, kv.Key.(*syntax.Name)))
    
    				elem = kv.Value
    			} else {
    				w.pos(elem)
    
    			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.expr(kv.Key)
    				elem = kv.Value
    			}
    		}
    		w.pos(elem)
    		w.expr(elem)
    	}
    }
    
    func (w *writer) funcLit(expr *syntax.FuncLit) {
    	tv, ok := w.p.info.Types[expr]
    	assert(ok)
    	sig := tv.Type.(*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)
    	}
    }
    
    
    func (w *writer) exprType(iface types2.Type, typ syntax.Expr, nilOK bool) {
    
    	base.Assertf(iface == nil || isInterface(iface), "%v must be nil or an interface type", iface)
    
    
    	tv, ok := w.p.info.Types[typ]
    	assert(ok)
    
    	w.Sync(pkgbits.SyncExprType)
    
    	if nilOK && w.Bool(tv.IsNil()) {
    		return
    	}
    
    	assert(tv.IsType())
    
    	info := w.p.typIdx(tv.Type, w.dict)
    
    
    
    	if w.Bool(info.derived && iface != nil && !iface.Underlying().(*types2.Interface).Empty()) {
    		ifaceInfo := w.p.typIdx(iface, w.dict)
    
    		idx := -1
    		for i, itab := range w.dict.itabs {
    			if itab.typIdx == info.idx && itab.iface == ifaceInfo {
    				idx = i
    			}
    		}
    		if idx < 0 {
    			idx = len(w.dict.itabs)
    			w.dict.itabs = append(w.dict.itabs, itabInfo{typIdx: info.idx, iface: ifaceInfo})
    		}
    		w.Len(idx)
    		return
    	}
    
    	w.typInfo(info)
    }
    
    
    // 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))
    
    func (w *writer) needType(typ types2.Type) {
    	// Decompose tuple into component element types.
    	if typ, ok := typ.(*types2.Tuple); ok {
    		for i := 0; i < typ.Len(); i++ {
    			w.needType(typ.At(i).Type())
    		}
    		return
    	}
    
    	if info := w.p.typIdx(typ, w.dict); info.derived {
    		w.dict.derived[info.idx].needed = true
    	}
    }
    
    
    // @@@ 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, typePragmas, 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:
    
    				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...)
    		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)
    
    		w.obj(obj, nil)
    	}
    }
    
    // @@@ Helpers
    
    // 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(info *types2.Info, 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, ok := info.Types[args[0]]
    			assert(ok)
    			if tv.IsValue() {
    				return // normal index expression
    
    			}
    		}
    
    		expr = index.X
    	}
    
    	// Strip package qualifier, if present.
    	if sel, ok := expr.(*syntax.SelectorExpr); ok {
    		if !isPkgQual(info, sel) {
    			return // normal selector expression
    		}
    		expr = sel.Sel
    	}
    
    	if name, ok := expr.(*syntax.Name); ok {
    
    		obj = info.Uses[name]
    		inst = 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 {