func checkVarValue(t *testing.T, prog *ssa.Program, pkg *ssa.Package, ref []ast.Node, obj *types.Var, expKind string, wantAddr bool) { // The prefix of all assertions messages. prefix := fmt.Sprintf("VarValue(%s @ L%d)", obj, prog.Fset.Position(ref[0].Pos()).Line) v := prog.VarValue(obj, pkg, ref) // Kind is the concrete type of the ssa Value. gotKind := "nil" if v != nil { gotKind = fmt.Sprintf("%T", v)[len("*ssa."):] } // fmt.Printf("%s = %v (kind %q; expect %q) addr=%t\n", prefix, v, gotKind, expKind, wantAddr) // debugging // Check the kinds match. // "nil" indicates expected failure (e.g. optimized away). if expKind != gotKind { t.Errorf("%s concrete type == %s, want %s", prefix, gotKind, expKind) } // Check the types match. // If wantAddr, the expected type is the object's address. if v != nil { expType := obj.Type() if wantAddr { expType = types.NewPointer(expType) } if !types.IsIdentical(v.Type(), expType) { t.Errorf("%s.Type() == %s, want %s", prefix, v.Type(), expType) } } }
func (p *exporter) field(f *types.Var) { // anonymous fields have "" name name := "" if !f.Anonymous() { name = f.Name() } // qualifiedName will always emit the field package for // anonymous fields because "" is not an exported name. p.qualifiedName(f.Pkg(), name) p.typ(f.Type()) }
// VarValue returns the SSA Value that corresponds to a specific // identifier denoting the source-level named variable obj. // // VarValue returns nil if a local variable was not found, perhaps // because its package was not built, the debug information was not // requested during SSA construction, or the value was optimized away. // // ref is the path to an ast.Ident (e.g. from PathEnclosingInterval), // and that ident must resolve to obj. // // pkg is the package enclosing the reference. (A reference to a var // may result in code, so we need to know where to find that code.) // // The Value of a defining (as opposed to referring) identifier is the // value assigned to it in its definition. // // In many cases where the identifier appears in an lvalue context, // the resulting Value is the var's address, not its value. // For example, x in all these examples: // x.y = 0 // x[0] = 0 // _ = x[:] // x = X{} // _ = &x // x.method() (iff method is on &x) // and all package-level vars. (This situation can be detected by // comparing the types of the Var and Value.) // func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) Value { id := ref[0].(*ast.Ident) // Package-level variable? if v := prog.packageLevelValue(obj); v != nil { return v.(*Global) } // Must be a function-local variable. // (e.g. local, parameter, or field selection e.f) fn := EnclosingFunction(pkg, ref) if fn == nil { return nil // e.g. SSA not built } // Defining ident of a parameter? if id.Pos() == obj.Pos() { for _, param := range fn.Params { if param.Object() == obj { return param } } } // Other ident? for _, b := range fn.Blocks { for _, instr := range b.Instrs { if ref, ok := instr.(*DebugRef); ok { if ref.Pos() == id.Pos() { return ref.X } } } } return nil // e.g. debug info not requested, or var optimized away }
// VarValue returns the SSA Value that corresponds to a specific // identifier denoting the source-level named variable obj. // // VarValue returns nil if a local variable was not found, perhaps // because its package was not built, the debug information was not // requested during SSA construction, or the value was optimized away. // // ref is the path to an ast.Ident (e.g. from PathEnclosingInterval), // and that ident must resolve to obj. // // pkg is the package enclosing the reference. (A reference to a var // always occurs within a function, so we need to know where to find it.) // // The Value of a defining (as opposed to referring) identifier is the // value assigned to it in its definition. Similarly, the Value of an // identifier that is the LHS of an assignment is the value assigned // to it in that statement. In all these examples, VarValue(x) returns // the value of x and isAddr==false. // // var x X // var x = X{} // x := X{} // x = X{} // // When an identifier appears in an lvalue context other than as the // LHS of an assignment, the resulting Value is the var's address, not // its value. This situation is reported by isAddr, the second // component of the result. In these examples, VarValue(x) returns // the address of x and isAddr==true. // // x.y = 0 // x[0] = 0 // _ = x[:] (where x is an array) // _ = &x // x.method() (iff method is on &x) // func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) { // All references to a var are local to some function, possibly init. fn := EnclosingFunction(pkg, ref) if fn == nil { return // e.g. def of struct field; SSA not built? } id := ref[0].(*ast.Ident) // Defining ident of a parameter? if id.Pos() == obj.Pos() { for _, param := range fn.Params { if param.Object() == obj { return param, false } } } // Other ident? for _, b := range fn.Blocks { for _, instr := range b.Instrs { if dr, ok := instr.(*DebugRef); ok { if dr.Pos() == id.Pos() { return dr.X, dr.IsAddr } } } } // Defining ident of package-level var? if v := prog.packageLevelValue(obj); v != nil { return v.(*Global), true } return // e.g. debug info not requested, or var optimized away }
func (tr *Transformer) matchWildcard(xobj *types.Var, y ast.Expr) bool { name := xobj.Name() if tr.verbose { fmt.Fprintf(os.Stderr, "%s: wildcard %s -> %s?: ", tr.fset.Position(y.Pos()), name, astString(tr.fset, y)) } // Check that y is assignable to the declared type of the param. if yt := tr.info.TypeOf(y); !types.AssignableTo(yt, xobj.Type()) { if tr.verbose { fmt.Fprintf(os.Stderr, "%s not assignable to %s\n", yt, xobj.Type()) } return false } // A wildcard matches any expression. // If it appears multiple times in the pattern, it must match // the same expression each time. if old, ok := tr.env[name]; ok { // found existing binding tr.allowWildcards = false r := tr.matchExpr(old, y) if tr.verbose { fmt.Fprintf(os.Stderr, "%t secondary match, primary was %s\n", r, astString(tr.fset, old)) } tr.allowWildcards = true return r } if tr.verbose { fmt.Fprintf(os.Stderr, "primary match\n") } tr.env[name] = y // record binding return true }
// checkStructField checks that the field renaming will not cause // conflicts at its declaration, or ambiguity or changes to any selection. func (r *renamer) checkStructField(from *types.Var) { // Check that the struct declaration is free of field conflicts, // and field/method conflicts. // go/types offers no easy way to get from a field (or interface // method) to its declaring struct (or interface), so we must // ascend the AST. info, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos()) // path is [Ident Field FieldList StructType ... File]. Can't fail. // Ascend past parens (unlikely). i := 4 for { _, ok := path[i].(*ast.ParenExpr) if !ok { break } i++ } if spec, ok := path[i].(*ast.TypeSpec); ok { // This struct is also a named type. // We must check for direct (non-promoted) field/field // and method/field conflicts. named := info.Defs[spec.Name].Type() prev, indices, _ := types.LookupFieldOrMethod(named, true, info.Pkg, r.to) if len(indices) == 1 { r.errorf(from.Pos(), "renaming this field %q to %q", from.Name(), r.to) r.errorf(prev.Pos(), "\twould conflict with this %s", objectKind(prev)) return // skip checkSelections to avoid redundant errors } } else { // This struct is not a named type. // We need only check for direct (non-promoted) field/field conflicts. T := info.Types[path[3].(*ast.StructType)].Type.Underlying().(*types.Struct) for i := 0; i < T.NumFields(); i++ { if prev := T.Field(i); prev.Name() == r.to { r.errorf(from.Pos(), "renaming this field %q to %q", from.Name(), r.to) r.errorf(prev.Pos(), "\twould conflict with this field") return // skip checkSelections to avoid redundant errors } } } // Renaming an anonymous field requires renaming the type too. e.g. // print(s.T) // if we rename T to U, // type T int // this and // var s struct {T} // this must change too. if from.Anonymous() { if named, ok := from.Type().(*types.Named); ok { r.check(named.Obj()) } else if named, ok := deref(from.Type()).(*types.Named); ok { r.check(named.Obj()) } } // Check integrity of existing (field and method) selections. r.checkSelections(from) }
func (g *Go) pkg_var(v *types.Var) (ret content.Variable) { ret.Name.Relative = dotre.ReplaceAllString(v.Name(), "") ret.Type = g.pkg_type(v.Type()) return }
func (p *exporter) param(v *types.Var) { p.string(v.Name()) p.typ(v.Type()) }
// makeWrapper returns a synthetic method that delegates to the // declared method denoted by meth.Obj(), first performing any // necessary pointer indirections or field selections implied by meth. // // The resulting method's receiver type is meth.Recv(). // // This function is versatile but quite subtle! Consider the // following axes of variation when making changes: // - optional receiver indirection // - optional implicit field selections // - meth.Obj() may denote a concrete or an interface method // - the result may be a thunk or a wrapper. // // EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu) // func makeWrapper(prog *Program, meth *types.Selection) *Function { obj := meth.Obj().(*types.Func) // the declared function sig := meth.Type().(*types.Signature) // type of this wrapper var recv *types.Var // wrapper's receiver or thunk's params[0] name := obj.Name() var description string var start int // first regular param if meth.Kind() == types.MethodExpr { name += "$thunk" description = "thunk" recv = sig.Params().At(0) start = 1 } else { description = "wrapper" recv = sig.Recv() } description = fmt.Sprintf("%s for %s", description, meth.Obj()) if prog.mode&LogSource != 0 { defer logStack("make %s to (%s)", description, recv.Type())() } fn := &Function{ name: name, method: meth, object: obj, Signature: sig, Synthetic: description, Prog: prog, pos: obj.Pos(), } fn.startBody() fn.addSpilledParam(recv) createParams(fn, start) indices := meth.Index() var v Value = fn.Locals[0] // spilled receiver if isPointer(meth.Recv()) { v = emitLoad(fn, v) // For simple indirection wrappers, perform an informative nil-check: // "value method (T).f called using nil *T pointer" if len(indices) == 1 && !isPointer(recvType(obj)) { var c Call c.Call.Value = &Builtin{ name: "ssa:wrapnilchk", sig: types.NewSignature(nil, nil, types.NewTuple(anonVar(meth.Recv()), anonVar(tString), anonVar(tString)), types.NewTuple(anonVar(meth.Recv())), false), } c.Call.Args = []Value{ v, stringConst(deref(meth.Recv()).String()), stringConst(meth.Obj().Name()), } c.setType(v.Type()) v = fn.emit(&c) } } // Invariant: v is a pointer, either // value of *A receiver param, or // address of A spilled receiver. // We use pointer arithmetic (FieldAddr possibly followed by // Load) in preference to value extraction (Field possibly // preceded by Load). v = emitImplicitSelections(fn, v, indices[:len(indices)-1]) // Invariant: v is a pointer, either // value of implicit *C field, or // address of implicit C field. var c Call if r := recvType(obj); !isInterface(r) { // concrete method if !isPointer(r) { v = emitLoad(fn, v) } c.Call.Value = prog.declaredFunc(obj) c.Call.Args = append(c.Call.Args, v) } else { c.Call.Method = obj c.Call.Value = emitLoad(fn, v) } for _, arg := range fn.Params[1:] { c.Call.Args = append(c.Call.Args, arg) } emitTailCall(fn, &c) fn.finishBody() return fn }