Skip to content
Snippets Groups Projects
writer.go 60 KiB
Newer Older
  • Learn to ignore specific revisions
  • func (dict *writerDict) methodExprIdx(recvInfo typeInfo, methodInfo selectorInfo) int {
    	assert(recvInfo.derived)
    	newInfo := methodExprInfo{recvIdx: recvInfo.idx, methodInfo: methodInfo}
    
    	for idx, oldInfo := range dict.methodExprs {
    		if oldInfo == newInfo {
    			return idx
    		}
    	}
    
    	idx := len(dict.methodExprs)
    	dict.methodExprs = append(dict.methodExprs, newInfo)
    	return idx
    }
    
    
    // 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, 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...)
    
    
    		// 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(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 {
    	if name, ok := sel.X.(*syntax.Name); ok {
    		_, isPkgName := info.Uses[name].(*types2.PkgName)
    		return isPkgName
    	}
    	return false
    }
    
    
    // isMultiValueExpr reports whether expr is a function call expression
    // that yields multiple values.
    func isMultiValueExpr(info *types2.Info, expr syntax.Expr) bool {
    	tv, ok := info.Types[expr]
    	assert(ok)
    	assert(tv.IsValue())
    	if tuple, ok := tv.Type.(*types2.Tuple); ok {
    		assert(tuple.Len() > 1)
    		return true
    	}
    	return false
    }
    
    
    // isNil reports whether expr is a (possibly parenthesized) reference
    // to the predeclared nil value.
    func isNil(info *types2.Info, expr syntax.Expr) bool {
    	tv, ok := info.Types[expr]
    	assert(ok)
    	return tv.IsNil()
    }
    
    
    // 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
    }