// lookup returns the address of the named variable identified by obj // that is local to function f or one of its enclosing functions. // If escaping, the reference comes from a potentially escaping pointer // expression and the referent must be heap-allocated. // func (f *Function) lookup(obj types.Object, escaping bool) Value { if v, ok := f.objects[obj]; ok { if alloc, ok := v.(*Alloc); ok && escaping { alloc.Heap = true } return v // function-local var (address) } // Definition must be in an enclosing function; // plumb it through intervening closures. if f.Enclosing == nil { panic("no Value for type.Object " + obj.Name()) } outer := f.Enclosing.lookup(obj, true) // escaping v := &Capture{ name: obj.Name(), typ: outer.Type(), pos: outer.Pos(), outer: outer, parent: f, } f.objects[obj] = v f.FreeVars = append(f.FreeVars, v) return v }
func formatMember(obj types.Object, maxname int) string { var buf bytes.Buffer fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name()) switch obj := obj.(type) { case *types.Const: fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Pkg(), obj.Type()), obj.Val().String()) case *types.Func: fmt.Fprintf(&buf, " %s", types.TypeString(obj.Pkg(), obj.Type())) case *types.TypeName: // Abbreviate long aggregate type names. var abbrev string switch t := obj.Type().Underlying().(type) { case *types.Interface: if t.NumMethods() > 1 { abbrev = "interface{...}" } case *types.Struct: if t.NumFields() > 1 { abbrev = "struct{...}" } } if abbrev == "" { fmt.Fprintf(&buf, " %s", types.TypeString(obj.Pkg(), obj.Type().Underlying())) } else { fmt.Fprintf(&buf, " %s", abbrev) } case *types.Var: fmt.Fprintf(&buf, " %s", types.TypeString(obj.Pkg(), obj.Type())) } return buf.String() }
func (c *funcContext) objectName(o types.Object) string { if o.Pkg() != c.p.pkg || o.Parent() == c.p.pkg.Scope() { c.p.dependencies[o] = true } if o.Pkg() != c.p.pkg { pkgVar, found := c.p.pkgVars[o.Pkg().Path()] if !found { pkgVar = fmt.Sprintf(`go$packages["%s"]`, o.Pkg().Path()) } return pkgVar + "." + o.Name() } name, found := c.p.objectVars[o] if !found { name = c.newVariable(o.Name()) c.p.objectVars[o] = name } switch o.(type) { case *types.Var, *types.Const: if o.Exported() && o.Parent() == c.p.pkg.Scope() { return "go$pkg." + name } } return name }
// isPackageLevel reports whether obj is a package-level object. func isPackageLevel(obj types.Object) bool { // TODO(adonovan): fix go/types bug: // obj.Parent().Parent() == obj.Pkg().Scope() // doesn't work because obj.Parent() gets mutated during // dot-imports. return obj.Pkg().Scope().Lookup(obj.Name()) == obj }
// memberFromObject populates package pkg with a member for the // typechecker object obj. // // For objects from Go source code, syntax is the associated syntax // tree (for funcs and vars only); it will be used during the build // phase. // func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { name := obj.Name() switch obj := obj.(type) { case *types.TypeName: pkg.Members[name] = &Type{object: obj} case *types.Const: c := &NamedConst{ object: obj, Value: NewConst(obj.Val(), obj.Type()), } pkg.values[obj] = c.Value pkg.Members[name] = c case *types.Var: spec, _ := syntax.(*ast.ValueSpec) g := &Global{ Pkg: pkg, name: name, object: obj, typ: types.NewPointer(obj.Type()), // address pos: obj.Pos(), spec: spec, } pkg.values[obj] = g pkg.Members[name] = g case *types.Func: var fs *funcSyntax synthetic := "loaded from gc object file" if decl, ok := syntax.(*ast.FuncDecl); ok { synthetic = "" fs = &funcSyntax{ functype: decl.Type, recvField: decl.Recv, body: decl.Body, } } fn := &Function{ name: name, object: obj, Signature: obj.Type().(*types.Signature), Synthetic: synthetic, pos: obj.Pos(), // (iff syntax) Pkg: pkg, Prog: pkg.Prog, syntax: fs, } pkg.values[obj] = fn if fn.Signature.Recv() == nil { pkg.Members[name] = fn // package-level function } default: // (incl. *types.Package) panic("unexpected Object type: " + obj.String()) } }
func (f *Function) addParamObj(obj types.Object) *Parameter { name := obj.Name() if name == "" { name = fmt.Sprintf("arg%d", len(f.Params)) } param := f.addParam(name, obj.Type(), obj.Pos()) param.object = obj return param }
// addSpilledParam declares a parameter that is pre-spilled to the // stack; the function body will load/store the spilled location. // Subsequent lifting will eliminate spills where possible. // func (f *Function) addSpilledParam(obj types.Object) { param := f.addParamObj(obj) spill := &Alloc{Comment: obj.Name()} spill.setType(types.NewPointer(obj.Type())) spill.setPos(obj.Pos()) f.objects[obj] = spill f.Locals = append(f.Locals, spill) f.emit(spill) f.emit(&Store{Addr: spill, Val: param}) }
func (r *resolver) defineObject(b *Block, name string, obj types.Object) { if obj.Name() == "_" { return } i := len(b.bindings) b.bindings = append(b.bindings, obj) b.index[name] = i if trace { logf("def %s = %s in %s\n", name, types.ObjectString(r.pkg, obj), b) } r.result.Defs[obj] = b }
func (p *printer) printObj(obj types.Object) { p.printf("%s", obj.Name()) // don't write untyped types (for constants) if typ := obj.Type(); typed(typ) { p.print(" ") p.writeType(p.pkg, typ) } // write constant value if obj, ok := obj.(*types.Const); ok { p.printf(" = %s", obj.Val()) } }
// memberFromObject populates package pkg with a member for the // typechecker object obj. // // For objects from Go source code, syntax is the associated syntax // tree (for funcs and vars only); it will be used during the build // phase. // func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { name := obj.Name() switch obj := obj.(type) { case *types.TypeName: pkg.values[obj] = nil // for needMethods pkg.Members[name] = &Type{ object: obj, pkg: pkg, } case *types.Const: c := &NamedConst{ object: obj, Value: NewConst(obj.Val(), obj.Type()), pkg: pkg, } pkg.values[obj] = c.Value pkg.Members[name] = c case *types.Var: g := &Global{ Pkg: pkg, name: name, object: obj, typ: types.NewPointer(obj.Type()), // address pos: obj.Pos(), } pkg.values[obj] = g pkg.Members[name] = g case *types.Func: fn := &Function{ name: name, object: obj, Signature: obj.Type().(*types.Signature), syntax: syntax, pos: obj.Pos(), // (iff syntax) Pkg: pkg, Prog: pkg.Prog, } if syntax == nil { fn.Synthetic = "loaded from gc object file" } pkg.values[obj] = fn if fn.Signature.Recv() == nil { pkg.Members[name] = fn // package-level function } default: // (incl. *types.Package) panic("unexpected Object type: " + obj.String()) } }
// addSpilledParam declares a parameter that is pre-spilled to the // stack; the function body will load/store the spilled location. // Subsequent lifting will eliminate spills where possible. // func (f *Function) addSpilledParam(obj types.Object) { name := obj.Name() param := f.addParam(name, obj.Type()) spill := &Alloc{ Name_: name + "~", // "~" means "spilled" Type_: pointer(obj.Type()), pos: obj.Pos(), } f.objects[obj] = spill f.Locals = append(f.Locals, spill) f.emit(spill) f.emit(&Store{Addr: spill, Val: param}) }
func (p *exporter) obj(obj types.Object) { if trace { p.tracef("object %s {\n", obj.Name()) defer p.tracef("}\n") } switch obj := obj.(type) { case *types.Const: p.int(constTag) p.string(obj.Name()) p.typ(obj.Type()) p.value(obj.Val()) case *types.TypeName: p.int(typeTag) // name is written by corresponding named type p.typ(obj.Type().(*types.Named)) case *types.Var: p.int(varTag) p.string(obj.Name()) p.typ(obj.Type()) case *types.Func: p.int(funcTag) p.string(obj.Name()) p.typ(obj.Type()) default: panic(fmt.Sprintf("unexpected object type %T", obj)) } }
func (r *renamer) checkExport(id *ast.Ident, pkg *types.Package, from types.Object) bool { // Reject cross-package references if r.to is unexported. // (Such references may be qualified identifiers or field/method // selections.) if !ast.IsExported(r.to) && pkg != from.Pkg() { r.errorf(from.Pos(), "renaming this %s %q to %q would make it unexported", objectKind(from), from.Name(), r.to) r.errorf(id.Pos(), "\tbreaking references from packages such as %q", pkg.Path()) return false } return true }
// ssaValueForIdent returns the ssa.Value for the ast.Ident whose path // to the root of the AST is path. isAddr reports whether the // ssa.Value is the address denoted by the ast.Ident, not its value. // func ssaValueForIdent(prog *ssa.Program, qinfo *importer.PackageInfo, obj types.Object, path []ast.Node) (value ssa.Value, isAddr bool, err error) { switch obj := obj.(type) { case *types.Var: pkg := prog.Package(qinfo.Pkg) pkg.Build() if v, addr := prog.VarValue(obj, pkg, path); v != nil { return v, addr, nil } return nil, false, fmt.Errorf("can't locate SSA Value for var %s", obj.Name()) case *types.Func: return prog.FuncValue(obj), false, nil } panic(obj) }
func (e *exporter) makeName(o types.Object) string { switch o.Name() { case "": return "?" case "_": return "_" default: pkgPath := "" if o.Pkg() != nil && o.Pkg() != e.pkg { e.addImport(o.Pkg()) pkgPath = o.Pkg().Path() } return `@"` + pkgPath + `".` + o.Name() } }
func (p *printer) printObj(obj types.Object) { p.print(obj.Name()) typ, basic := obj.Type().Underlying().(*types.Basic) if basic && typ.Info()&types.IsUntyped != 0 { // don't write untyped types } else { p.print(" ") p.writeType(p.pkg, obj.Type()) } if obj, ok := obj.(*types.Const); ok { floatFmt := basic && typ.Info()&(types.IsFloat|types.IsComplex) != 0 p.print(" = ") p.print(valString(obj.Val(), floatFmt)) } }
func (cdd *CDD) Name(w *bytes.Buffer, obj types.Object, direct bool) { if obj == nil { w.WriteByte('_') return } switch o := obj.(type) { case *types.PkgName: // Imported package name in SelectorExpr: pkgname.Name w.WriteString(upath(o.Pkg().Path())) return case *types.Func: s := o.Type().(*types.Signature) if r := s.Recv(); r != nil { t := r.Type() if p, ok := t.(*types.Pointer); ok { t = p.Elem() direct = false } cdd.Type(w, t) w.WriteByte('$') w.WriteString(o.Name()) if !cdd.gtc.isLocal(t.(*types.Named).Obj()) { cdd.addObject(o, direct) } return } } if p := obj.Pkg(); p != nil && !cdd.gtc.isLocal(obj) { cdd.addObject(obj, direct) w.WriteString(upath(obj.Pkg().Path())) w.WriteByte('$') } name := obj.Name() switch name { case "init": w.WriteString(cdd.gtc.uniqueId() + name) default: w.WriteString(name) if cdd.gtc.isLocal(obj) { w.WriteByte('$') } } }
// ssaValueForIdent returns the ssa.Value for the ast.Ident whose path // to the root of the AST is path. It may return a nil Value without // an error to indicate the pointer analysis is not appropriate. // func ssaValueForIdent(prog *ssa.Program, qinfo *importer.PackageInfo, obj types.Object, path []ast.Node) (ssa.Value, error) { if obj, ok := obj.(*types.Var); ok { pkg := prog.Package(qinfo.Pkg) pkg.Build() if v := prog.VarValue(obj, pkg, path); v != nil { // Don't run pointer analysis on a ref to a const expression. if _, ok := v.(*ssa.Const); ok { v = nil } return v, nil } return nil, fmt.Errorf("can't locate SSA Value for var %s", obj.Name()) } // Don't run pointer analysis on const/func objects. return nil, nil }
func (e *exporter) makeName(o types.Object) string { if o.Name() == "" || o.Name() == "_" { return "?" } if o.Pkg() == nil || o.Pkg() == e.pkg { return `@"".` + o.Name() } e.addImport(o.Pkg()) return `@"` + o.Pkg().Path() + `".` + o.Name() }
func (w *Walker) emitObj(obj types.Object) { switch obj := obj.(type) { case *types.Const: w.emitf("const %s %s", obj.Name(), w.typeString(obj.Type())) case *types.Var: w.emitf("var %s %s", obj.Name(), w.typeString(obj.Type())) case *types.TypeName: w.emitType(obj) case *types.Func: w.emitFunc(obj) default: panic("unknown object: " + obj.String()) } }
// createLocalVariableMetadata creates and returns a debug descriptor for // a local variable in a function. // // obj is the go/types object for the var. // paramIndex is 0 for "auto" variables (non-parameter stack vars), and a // 1-based index for parameter vars. func (c *compiler) createLocalVariableMetadata(obj types.Object, paramIndex int) llvm.DebugDescriptor { ctx := c.currentDebugContext() if ctx == nil { return nil } tag := llvm.DW_TAG_auto_variable if paramIndex > 0 { tag = llvm.DW_TAG_arg_variable } position := c.fileset.Position(obj.Pos()) ld := llvm.NewLocalVariableDescriptor(tag) ld.Argument = uint32(paramIndex) ld.Line = uint32(position.Line) ld.Name = obj.Name() ld.File = &llvm.ContextDescriptor{llvm.FileDescriptor(position.Filename)} ld.Type = c.tollvmDebugDescriptor(obj.Type()) ld.Context = ctx return ld }
// ssaValueForIdent returns the ssa.Value for the ast.Ident whose path // to the root of the AST is path. isAddr reports whether the // ssa.Value is the address denoted by the ast.Ident, not its value. // func ssaValueForIdent(prog *ssa.Program, qinfo *loader.PackageInfo, obj types.Object, path []ast.Node) (value ssa.Value, isAddr bool, err error) { switch obj := obj.(type) { case *types.Var: pkg := prog.Package(qinfo.Pkg) pkg.Build() if v, addr := prog.VarValue(obj, pkg, path); v != nil { return v, addr, nil } return nil, false, fmt.Errorf("can't locate SSA Value for var %s", obj.Name()) case *types.Func: fn := prog.FuncValue(obj) if fn == nil { return nil, false, fmt.Errorf("%s is an interface method", obj) } // TODO(adonovan): there's no point running PTA on a *Func ident. // Eliminate this feature. return fn, false, nil } panic(obj) }
func typeObjectToJson(o *types.Object) interface{} { switch o := (*o).(type) { case *types.Package: return typePackageToJson(o) case *types.Const: return struct { Isa string Pkg interface{} Name string Type interface{} Val interface{} }{ "Const", typePackageToJson(o.Pkg()), o.Name(), typeTypeToJson(o.Type()), o.Val(), } case *types.TypeName: return struct { Isa string Pkg interface{} Name string Type interface{} }{ "TypeName", typePackageToJson(o.Pkg()), o.Name(), typeTypeToJson(o.Type()), } case *types.Var: return struct { Isa string Pkg interface{} Name string Type interface{} }{ "Var", typePackageToJson(o.Pkg()), o.Name(), typeTypeToJson(o.Type()), } case *types.Func: return struct { Isa string Pkg interface{} Name string Type interface{} }{ "Func", typePackageToJson(o.Pkg()), o.Name(), typeTypeToJson(o.Type()), } default: if o != nil { return nil } else { return "UNKNOWN" } } return nil }
func (f *File) checkNilFuncComparison(e *ast.BinaryExpr) { if !vet("nilfunc") { return } // Only want == or != comparisons. if e.Op != token.EQL && e.Op != token.NEQ { return } // Only want comparisons with a nil identifier on one side. var e2 ast.Expr switch { case f.isNil(e.X): e2 = e.Y case f.isNil(e.Y): e2 = e.X default: return } // Only want identifiers or selector expressions. var obj types.Object switch v := e2.(type) { case *ast.Ident: obj = f.pkg.idents[v] case *ast.SelectorExpr: obj = f.pkg.idents[v.Sel] default: return } // Only want functions. if _, ok := obj.(*types.Func); !ok { return } f.Badf(e.Pos(), "comparison of function %v %v nil is always %v", obj.Name(), e.Op, e.Op == token.NEQ) }
func (c *PkgContext) objectName(o types.Object) string { if o.Pkg() != nil && o.Pkg() != c.pkg { pkgVar, found := c.pkgVars[o.Pkg().Path()] if !found { pkgVar = fmt.Sprintf(`Go$packages["%s"]`, o.Pkg().Path()) } return pkgVar + "." + o.Name() } name, found := c.objectVars[o] if !found { name = c.newVariable(o.Name()) c.objectVars[o] = name } switch o.(type) { case *types.Var, *types.Const: if o.Parent() == c.pkg.Scope() { return "Go$pkg." + name } } return name }
func (r *renamer) selectionConflict(from types.Object, delta int, syntax *ast.SelectorExpr, obj types.Object) { r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) switch { case delta < 0: // analogous to sub-block conflict r.errorf(syntax.Sel.Pos(), "\twould change the referent of this selection") r.errorf(obj.Pos(), "\tto this %s", objectKind(obj)) case delta == 0: // analogous to same-block conflict r.errorf(syntax.Sel.Pos(), "\twould make this reference ambiguous") r.errorf(obj.Pos(), "\twith this %s", objectKind(obj)) case delta > 0: // analogous to super-block conflict r.errorf(syntax.Sel.Pos(), "\twould shadow this selection") r.errorf(obj.Pos(), "\tto the %s declared here", objectKind(obj)) } }
// checkInPackageBlock performs safety checks for renames of // func/var/const/type objects in the package block. func (r *renamer) checkInPackageBlock(from types.Object) { // Check that there are no references to the name from another // package if the renaming would make it unexported. if ast.IsExported(from.Name()) && !ast.IsExported(r.to) { for pkg, info := range r.packages { if pkg == from.Pkg() { continue } if id := someUse(info, from); id != nil && !r.checkExport(id, pkg, from) { break } } } info := r.packages[from.Pkg()] lexinfo := lexical.Structure(r.iprog.Fset, from.Pkg(), &info.Info, info.Files) // Check that in the package block, "init" is a function, and never referenced. if r.to == "init" { kind := objectKind(from) if kind == "func" { // Reject if intra-package references to it exist. if refs := lexinfo.Refs[from]; len(refs) > 0 { r.errorf(from.Pos(), "renaming this func %q to %q would make it a package initializer", from.Name(), r.to) r.errorf(refs[0].Id.Pos(), "\tbut references to it exist") } } else { r.errorf(from.Pos(), "you cannot have a %s at package level named %q", kind, r.to) } } // Check for conflicts between package block and all file blocks. for _, f := range info.Files { if prev, b := lexinfo.Blocks[f].Lookup(r.to); b == lexinfo.Blocks[f] { r.errorf(from.Pos(), "renaming this %s %q to %q would conflict", objectKind(from), from.Name(), r.to) r.errorf(prev.Pos(), "\twith this %s", objectKind(prev)) return // since checkInPackageBlock would report redundant errors } } // Check for conflicts in lexical scope. if from.Exported() { for _, info := range r.packages { r.checkInLexicalScope(from, info) } } else { r.checkInLexicalScope(from, info) } }
func isAccessibleFrom(obj types.Object, pkg *types.Package) bool { return ast.IsExported(obj.Name()) || obj.Pkg() == pkg }
// checkSelection checks that all uses and selections that resolve to // the specified object would continue to do so after the renaming. func (r *renamer) checkSelections(from types.Object) { for pkg, info := range r.packages { if id := someUse(info, from); id != nil { if !r.checkExport(id, pkg, from) { return } } for syntax, sel := range info.Selections { // There may be extant selections of only the old // name or only the new name, so we must check both. // (If neither, the renaming is sound.) // // In both cases, we wish to compare the lengths // of the implicit field path (Selection.Index) // to see if the renaming would change it. // // If a selection that resolves to 'from', when renamed, // would yield a path of the same or shorter length, // this indicates ambiguity or a changed referent, // analogous to same- or sub-block lexical conflict. // // If a selection using the name 'to' would // yield a path of the same or shorter length, // this indicates ambiguity or shadowing, // analogous to same- or super-block lexical conflict. // TODO(adonovan): fix: derive from Types[syntax.X].Mode // TODO(adonovan): test with pointer, value, addressable value. isAddressable := true if sel.Obj() == from { if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), r.to); obj != nil { // Renaming this existing selection of // 'from' may block access to an existing // type member named 'to'. delta := len(indices) - len(sel.Index()) if delta > 0 { continue // no ambiguity } r.selectionConflict(from, delta, syntax, obj) return } } else if sel.Obj().Name() == r.to { if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), from.Name()); obj == from { // Renaming 'from' may cause this existing // selection of the name 'to' to change // its meaning. delta := len(indices) - len(sel.Index()) if delta > 0 { continue // no ambiguity } r.selectionConflict(from, -delta, syntax, sel.Obj()) return } } } } }
// checkInLexicalScope performs safety checks that a renaming does not // change the lexical reference structure of the specified package. // // For objects in lexical scope, there are three kinds of conflicts: // same-, sub-, and super-block conflicts. We will illustrate all three // using this example: // // var x int // var z int // // func f(y int) { // print(x) // print(y) // } // // Renaming x to z encounters a SAME-BLOCK CONFLICT, because an object // with the new name already exists, defined in the same lexical block // as the old object. // // Renaming x to y encounters a SUB-BLOCK CONFLICT, because there exists // a reference to x from within (what would become) a hole in its scope. // The definition of y in an (inner) sub-block would cast a shadow in // the scope of the renamed variable. // // Renaming y to x encounters a SUPER-BLOCK CONFLICT. This is the // converse situation: there is an existing definition of the new name // (x) in an (enclosing) super-block, and the renaming would create a // hole in its scope, within which there exist references to it. The // new name casts a shadow in scope of the existing definition of x in // the super-block. // // Removing the old name (and all references to it) is always safe, and // requires no checks. // func (r *renamer) checkInLexicalScope(from types.Object, info *loader.PackageInfo) { lexinfo := lexical.Structure(r.iprog.Fset, info.Pkg, &info.Info, info.Files) b := lexinfo.Defs[from] // the block defining the 'from' object if b != nil { to, toBlock := b.Lookup(r.to) if toBlock == b { // same-block conflict r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) r.errorf(to.Pos(), "\tconflicts with %s in same block", objectKind(to)) return } else if toBlock != nil { // Check for super-block conflict. // The name r.to is defined in a superblock. // Is that name referenced from within this block? for _, ref := range lexinfo.Refs[to] { if obj, _ := ref.Env.Lookup(from.Name()); obj == from { // super-block conflict r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) r.errorf(ref.Id.Pos(), "\twould shadow this reference") r.errorf(to.Pos(), "\tto the %s declared here", objectKind(to)) return } } } } // Check for sub-block conflict. // Is there an intervening definition of r.to between // the block defining 'from' and some reference to it? for _, ref := range lexinfo.Refs[from] { // TODO(adonovan): think about dot imports. // (Is b == fromBlock an invariant?) _, fromBlock := ref.Env.Lookup(from.Name()) fromDepth := fromBlock.Depth() to, toBlock := ref.Env.Lookup(r.to) if to != nil { // sub-block conflict if toBlock.Depth() > fromDepth { r.errorf(from.Pos(), "renaming this %s %q to %q", objectKind(from), from.Name(), r.to) r.errorf(ref.Id.Pos(), "\twould cause this reference to become shadowed") r.errorf(to.Pos(), "\tby this intervening %s definition", objectKind(to)) return } } } // Renaming a type that is used as an embedded field // requires renaming the field too. e.g. // type T int // if we rename this to U.. // var s struct {T} // print(s.T) // ...this must change too if _, ok := from.(*types.TypeName); ok { for id, obj := range info.Uses { if obj == from { if field := info.Defs[id]; field != nil { r.check(field) } } } } }