1// Copyright 2013 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// Package ssa/interp defines an interpreter for the SSA 6// representation of Go programs. 7// 8// This interpreter is provided as an adjunct for testing the SSA 9// construction algorithm. Its purpose is to provide a minimal 10// metacircular implementation of the dynamic semantics of each SSA 11// instruction. It is not, and will never be, a production-quality Go 12// interpreter. 13// 14// The following is a partial list of Go features that are currently 15// unsupported or incomplete in the interpreter. 16// 17// * Unsafe operations, including all uses of unsafe.Pointer, are 18// impossible to support given the "boxed" value representation we 19// have chosen. 20// 21// * The reflect package is only partially implemented. 22// 23// * The "testing" package is no longer supported because it 24// depends on low-level details that change too often. 25// 26// * "sync/atomic" operations are not atomic due to the "boxed" value 27// representation: it is not possible to read, modify and write an 28// interface value atomically. As a consequence, Mutexes are currently 29// broken. 30// 31// * recover is only partially implemented. Also, the interpreter 32// makes no attempt to distinguish target panics from interpreter 33// crashes. 34// 35// * map iteration is asymptotically inefficient. 36// 37// * the sizes of the int, uint and uintptr types in the target 38// program are assumed to be the same as those of the interpreter 39// itself. 40// 41// * all values occupy space, even those of types defined by the spec 42// to have zero size, e.g. struct{}. This can cause asymptotic 43// performance degradation. 44// 45// * os.Exit is implemented using panic, causing deferred functions to 46// run. 47package interp // import "golang.org/x/tools/go/ssa/interp" 48 49import ( 50 "fmt" 51 "go/token" 52 "go/types" 53 "os" 54 "reflect" 55 "runtime" 56 "sync/atomic" 57 58 "golang.org/x/tools/go/ssa" 59) 60 61type continuation int 62 63const ( 64 kNext continuation = iota 65 kReturn 66 kJump 67) 68 69// Mode is a bitmask of options affecting the interpreter. 70type Mode uint 71 72const ( 73 DisableRecover Mode = 1 << iota // Disable recover() in target programs; show interpreter crash instead. 74 EnableTracing // Print a trace of all instructions as they are interpreted. 75) 76 77type methodSet map[string]*ssa.Function 78 79// State shared between all interpreted goroutines. 80type interpreter struct { 81 osArgs []value // the value of os.Args 82 prog *ssa.Program // the SSA program 83 globals map[ssa.Value]*value // addresses of global variables (immutable) 84 mode Mode // interpreter options 85 reflectPackage *ssa.Package // the fake reflect package 86 errorMethods methodSet // the method set of reflect.error, which implements the error interface. 87 rtypeMethods methodSet // the method set of rtype, which implements the reflect.Type interface. 88 runtimeErrorString types.Type // the runtime.errorString type 89 sizes types.Sizes // the effective type-sizing function 90 goroutines int32 // atomically updated 91} 92 93type deferred struct { 94 fn value 95 args []value 96 instr *ssa.Defer 97 tail *deferred 98} 99 100type frame struct { 101 i *interpreter 102 caller *frame 103 fn *ssa.Function 104 block, prevBlock *ssa.BasicBlock 105 env map[ssa.Value]value // dynamic values of SSA variables 106 locals []value 107 defers *deferred 108 result value 109 panicking bool 110 panic interface{} 111} 112 113func (fr *frame) get(key ssa.Value) value { 114 switch key := key.(type) { 115 case nil: 116 // Hack; simplifies handling of optional attributes 117 // such as ssa.Slice.{Low,High}. 118 return nil 119 case *ssa.Function, *ssa.Builtin: 120 return key 121 case *ssa.Const: 122 return constValue(key) 123 case *ssa.Global: 124 if r, ok := fr.i.globals[key]; ok { 125 return r 126 } 127 } 128 if r, ok := fr.env[key]; ok { 129 return r 130 } 131 panic(fmt.Sprintf("get: no value for %T: %v", key, key.Name())) 132} 133 134// runDefer runs a deferred call d. 135// It always returns normally, but may set or clear fr.panic. 136// 137func (fr *frame) runDefer(d *deferred) { 138 if fr.i.mode&EnableTracing != 0 { 139 fmt.Fprintf(os.Stderr, "%s: invoking deferred function call\n", 140 fr.i.prog.Fset.Position(d.instr.Pos())) 141 } 142 var ok bool 143 defer func() { 144 if !ok { 145 // Deferred call created a new state of panic. 146 fr.panicking = true 147 fr.panic = recover() 148 } 149 }() 150 call(fr.i, fr, d.instr.Pos(), d.fn, d.args) 151 ok = true 152} 153 154// runDefers executes fr's deferred function calls in LIFO order. 155// 156// On entry, fr.panicking indicates a state of panic; if 157// true, fr.panic contains the panic value. 158// 159// On completion, if a deferred call started a panic, or if no 160// deferred call recovered from a previous state of panic, then 161// runDefers itself panics after the last deferred call has run. 162// 163// If there was no initial state of panic, or it was recovered from, 164// runDefers returns normally. 165// 166func (fr *frame) runDefers() { 167 for d := fr.defers; d != nil; d = d.tail { 168 fr.runDefer(d) 169 } 170 fr.defers = nil 171 if fr.panicking { 172 panic(fr.panic) // new panic, or still panicking 173 } 174} 175 176// lookupMethod returns the method set for type typ, which may be one 177// of the interpreter's fake types. 178func lookupMethod(i *interpreter, typ types.Type, meth *types.Func) *ssa.Function { 179 switch typ { 180 case rtypeType: 181 return i.rtypeMethods[meth.Id()] 182 case errorType: 183 return i.errorMethods[meth.Id()] 184 } 185 return i.prog.LookupMethod(typ, meth.Pkg(), meth.Name()) 186} 187 188// visitInstr interprets a single ssa.Instruction within the activation 189// record frame. It returns a continuation value indicating where to 190// read the next instruction from. 191func visitInstr(fr *frame, instr ssa.Instruction) continuation { 192 switch instr := instr.(type) { 193 case *ssa.DebugRef: 194 // no-op 195 196 case *ssa.UnOp: 197 fr.env[instr] = unop(instr, fr.get(instr.X)) 198 199 case *ssa.BinOp: 200 fr.env[instr] = binop(instr.Op, instr.X.Type(), fr.get(instr.X), fr.get(instr.Y)) 201 202 case *ssa.Call: 203 fn, args := prepareCall(fr, &instr.Call) 204 fr.env[instr] = call(fr.i, fr, instr.Pos(), fn, args) 205 206 case *ssa.ChangeInterface: 207 fr.env[instr] = fr.get(instr.X) 208 209 case *ssa.ChangeType: 210 fr.env[instr] = fr.get(instr.X) // (can't fail) 211 212 case *ssa.Convert: 213 fr.env[instr] = conv(instr.Type(), instr.X.Type(), fr.get(instr.X)) 214 215 case *ssa.MakeInterface: 216 fr.env[instr] = iface{t: instr.X.Type(), v: fr.get(instr.X)} 217 218 case *ssa.Extract: 219 fr.env[instr] = fr.get(instr.Tuple).(tuple)[instr.Index] 220 221 case *ssa.Slice: 222 fr.env[instr] = slice(fr.get(instr.X), fr.get(instr.Low), fr.get(instr.High), fr.get(instr.Max)) 223 224 case *ssa.Return: 225 switch len(instr.Results) { 226 case 0: 227 case 1: 228 fr.result = fr.get(instr.Results[0]) 229 default: 230 var res []value 231 for _, r := range instr.Results { 232 res = append(res, fr.get(r)) 233 } 234 fr.result = tuple(res) 235 } 236 fr.block = nil 237 return kReturn 238 239 case *ssa.RunDefers: 240 fr.runDefers() 241 242 case *ssa.Panic: 243 panic(targetPanic{fr.get(instr.X)}) 244 245 case *ssa.Send: 246 fr.get(instr.Chan).(chan value) <- fr.get(instr.X) 247 248 case *ssa.Store: 249 store(deref(instr.Addr.Type()), fr.get(instr.Addr).(*value), fr.get(instr.Val)) 250 251 case *ssa.If: 252 succ := 1 253 if fr.get(instr.Cond).(bool) { 254 succ = 0 255 } 256 fr.prevBlock, fr.block = fr.block, fr.block.Succs[succ] 257 return kJump 258 259 case *ssa.Jump: 260 fr.prevBlock, fr.block = fr.block, fr.block.Succs[0] 261 return kJump 262 263 case *ssa.Defer: 264 fn, args := prepareCall(fr, &instr.Call) 265 fr.defers = &deferred{ 266 fn: fn, 267 args: args, 268 instr: instr, 269 tail: fr.defers, 270 } 271 272 case *ssa.Go: 273 fn, args := prepareCall(fr, &instr.Call) 274 atomic.AddInt32(&fr.i.goroutines, 1) 275 go func() { 276 call(fr.i, nil, instr.Pos(), fn, args) 277 atomic.AddInt32(&fr.i.goroutines, -1) 278 }() 279 280 case *ssa.MakeChan: 281 fr.env[instr] = make(chan value, asInt(fr.get(instr.Size))) 282 283 case *ssa.Alloc: 284 var addr *value 285 if instr.Heap { 286 // new 287 addr = new(value) 288 fr.env[instr] = addr 289 } else { 290 // local 291 addr = fr.env[instr].(*value) 292 } 293 *addr = zero(deref(instr.Type())) 294 295 case *ssa.MakeSlice: 296 slice := make([]value, asInt(fr.get(instr.Cap))) 297 tElt := instr.Type().Underlying().(*types.Slice).Elem() 298 for i := range slice { 299 slice[i] = zero(tElt) 300 } 301 fr.env[instr] = slice[:asInt(fr.get(instr.Len))] 302 303 case *ssa.MakeMap: 304 reserve := 0 305 if instr.Reserve != nil { 306 reserve = asInt(fr.get(instr.Reserve)) 307 } 308 fr.env[instr] = makeMap(instr.Type().Underlying().(*types.Map).Key(), reserve) 309 310 case *ssa.Range: 311 fr.env[instr] = rangeIter(fr.get(instr.X), instr.X.Type()) 312 313 case *ssa.Next: 314 fr.env[instr] = fr.get(instr.Iter).(iter).next() 315 316 case *ssa.FieldAddr: 317 fr.env[instr] = &(*fr.get(instr.X).(*value)).(structure)[instr.Field] 318 319 case *ssa.Field: 320 fr.env[instr] = fr.get(instr.X).(structure)[instr.Field] 321 322 case *ssa.IndexAddr: 323 x := fr.get(instr.X) 324 idx := fr.get(instr.Index) 325 switch x := x.(type) { 326 case []value: 327 fr.env[instr] = &x[asInt(idx)] 328 case *value: // *array 329 fr.env[instr] = &(*x).(array)[asInt(idx)] 330 default: 331 panic(fmt.Sprintf("unexpected x type in IndexAddr: %T", x)) 332 } 333 334 case *ssa.Index: 335 fr.env[instr] = fr.get(instr.X).(array)[asInt(fr.get(instr.Index))] 336 337 case *ssa.Lookup: 338 fr.env[instr] = lookup(instr, fr.get(instr.X), fr.get(instr.Index)) 339 340 case *ssa.MapUpdate: 341 m := fr.get(instr.Map) 342 key := fr.get(instr.Key) 343 v := fr.get(instr.Value) 344 switch m := m.(type) { 345 case map[value]value: 346 m[key] = v 347 case *hashmap: 348 m.insert(key.(hashable), v) 349 default: 350 panic(fmt.Sprintf("illegal map type: %T", m)) 351 } 352 353 case *ssa.TypeAssert: 354 fr.env[instr] = typeAssert(fr.i, instr, fr.get(instr.X).(iface)) 355 356 case *ssa.MakeClosure: 357 var bindings []value 358 for _, binding := range instr.Bindings { 359 bindings = append(bindings, fr.get(binding)) 360 } 361 fr.env[instr] = &closure{instr.Fn.(*ssa.Function), bindings} 362 363 case *ssa.Phi: 364 for i, pred := range instr.Block().Preds { 365 if fr.prevBlock == pred { 366 fr.env[instr] = fr.get(instr.Edges[i]) 367 break 368 } 369 } 370 371 case *ssa.Select: 372 var cases []reflect.SelectCase 373 if !instr.Blocking { 374 cases = append(cases, reflect.SelectCase{ 375 Dir: reflect.SelectDefault, 376 }) 377 } 378 for _, state := range instr.States { 379 var dir reflect.SelectDir 380 if state.Dir == types.RecvOnly { 381 dir = reflect.SelectRecv 382 } else { 383 dir = reflect.SelectSend 384 } 385 var send reflect.Value 386 if state.Send != nil { 387 send = reflect.ValueOf(fr.get(state.Send)) 388 } 389 cases = append(cases, reflect.SelectCase{ 390 Dir: dir, 391 Chan: reflect.ValueOf(fr.get(state.Chan)), 392 Send: send, 393 }) 394 } 395 chosen, recv, recvOk := reflect.Select(cases) 396 if !instr.Blocking { 397 chosen-- // default case should have index -1. 398 } 399 r := tuple{chosen, recvOk} 400 for i, st := range instr.States { 401 if st.Dir == types.RecvOnly { 402 var v value 403 if i == chosen && recvOk { 404 // No need to copy since send makes an unaliased copy. 405 v = recv.Interface().(value) 406 } else { 407 v = zero(st.Chan.Type().Underlying().(*types.Chan).Elem()) 408 } 409 r = append(r, v) 410 } 411 } 412 fr.env[instr] = r 413 414 default: 415 panic(fmt.Sprintf("unexpected instruction: %T", instr)) 416 } 417 418 // if val, ok := instr.(ssa.Value); ok { 419 // fmt.Println(toString(fr.env[val])) // debugging 420 // } 421 422 return kNext 423} 424 425// prepareCall determines the function value and argument values for a 426// function call in a Call, Go or Defer instruction, performing 427// interface method lookup if needed. 428// 429func prepareCall(fr *frame, call *ssa.CallCommon) (fn value, args []value) { 430 v := fr.get(call.Value) 431 if call.Method == nil { 432 // Function call. 433 fn = v 434 } else { 435 // Interface method invocation. 436 recv := v.(iface) 437 if recv.t == nil { 438 panic("method invoked on nil interface") 439 } 440 if f := lookupMethod(fr.i, recv.t, call.Method); f == nil { 441 // Unreachable in well-typed programs. 442 panic(fmt.Sprintf("method set for dynamic type %v does not contain %s", recv.t, call.Method)) 443 } else { 444 fn = f 445 } 446 args = append(args, recv.v) 447 } 448 for _, arg := range call.Args { 449 args = append(args, fr.get(arg)) 450 } 451 return 452} 453 454// call interprets a call to a function (function, builtin or closure) 455// fn with arguments args, returning its result. 456// callpos is the position of the callsite. 457// 458func call(i *interpreter, caller *frame, callpos token.Pos, fn value, args []value) value { 459 switch fn := fn.(type) { 460 case *ssa.Function: 461 if fn == nil { 462 panic("call of nil function") // nil of func type 463 } 464 return callSSA(i, caller, callpos, fn, args, nil) 465 case *closure: 466 return callSSA(i, caller, callpos, fn.Fn, args, fn.Env) 467 case *ssa.Builtin: 468 return callBuiltin(caller, callpos, fn, args) 469 } 470 panic(fmt.Sprintf("cannot call %T", fn)) 471} 472 473func loc(fset *token.FileSet, pos token.Pos) string { 474 if pos == token.NoPos { 475 return "" 476 } 477 return " at " + fset.Position(pos).String() 478} 479 480// callSSA interprets a call to function fn with arguments args, 481// and lexical environment env, returning its result. 482// callpos is the position of the callsite. 483// 484func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function, args []value, env []value) value { 485 if i.mode&EnableTracing != 0 { 486 fset := fn.Prog.Fset 487 // TODO(adonovan): fix: loc() lies for external functions. 488 fmt.Fprintf(os.Stderr, "Entering %s%s.\n", fn, loc(fset, fn.Pos())) 489 suffix := "" 490 if caller != nil { 491 suffix = ", resuming " + caller.fn.String() + loc(fset, callpos) 492 } 493 defer fmt.Fprintf(os.Stderr, "Leaving %s%s.\n", fn, suffix) 494 } 495 fr := &frame{ 496 i: i, 497 caller: caller, // for panic/recover 498 fn: fn, 499 } 500 if fn.Parent() == nil { 501 name := fn.String() 502 if ext := externals[name]; ext != nil { 503 if i.mode&EnableTracing != 0 { 504 fmt.Fprintln(os.Stderr, "\t(external)") 505 } 506 return ext(fr, args) 507 } 508 if fn.Blocks == nil { 509 panic("no code for function: " + name) 510 } 511 } 512 fr.env = make(map[ssa.Value]value) 513 fr.block = fn.Blocks[0] 514 fr.locals = make([]value, len(fn.Locals)) 515 for i, l := range fn.Locals { 516 fr.locals[i] = zero(deref(l.Type())) 517 fr.env[l] = &fr.locals[i] 518 } 519 for i, p := range fn.Params { 520 fr.env[p] = args[i] 521 } 522 for i, fv := range fn.FreeVars { 523 fr.env[fv] = env[i] 524 } 525 for fr.block != nil { 526 runFrame(fr) 527 } 528 // Destroy the locals to avoid accidental use after return. 529 for i := range fn.Locals { 530 fr.locals[i] = bad{} 531 } 532 return fr.result 533} 534 535// runFrame executes SSA instructions starting at fr.block and 536// continuing until a return, a panic, or a recovered panic. 537// 538// After a panic, runFrame panics. 539// 540// After a normal return, fr.result contains the result of the call 541// and fr.block is nil. 542// 543// A recovered panic in a function without named return parameters 544// (NRPs) becomes a normal return of the zero value of the function's 545// result type. 546// 547// After a recovered panic in a function with NRPs, fr.result is 548// undefined and fr.block contains the block at which to resume 549// control. 550// 551func runFrame(fr *frame) { 552 defer func() { 553 if fr.block == nil { 554 return // normal return 555 } 556 if fr.i.mode&DisableRecover != 0 { 557 return // let interpreter crash 558 } 559 fr.panicking = true 560 fr.panic = recover() 561 if fr.i.mode&EnableTracing != 0 { 562 fmt.Fprintf(os.Stderr, "Panicking: %T %v.\n", fr.panic, fr.panic) 563 } 564 fr.runDefers() 565 fr.block = fr.fn.Recover 566 }() 567 568 for { 569 if fr.i.mode&EnableTracing != 0 { 570 fmt.Fprintf(os.Stderr, ".%s:\n", fr.block) 571 } 572 block: 573 for _, instr := range fr.block.Instrs { 574 if fr.i.mode&EnableTracing != 0 { 575 if v, ok := instr.(ssa.Value); ok { 576 fmt.Fprintln(os.Stderr, "\t", v.Name(), "=", instr) 577 } else { 578 fmt.Fprintln(os.Stderr, "\t", instr) 579 } 580 } 581 switch visitInstr(fr, instr) { 582 case kReturn: 583 return 584 case kNext: 585 // no-op 586 case kJump: 587 break block 588 } 589 } 590 } 591} 592 593// doRecover implements the recover() built-in. 594func doRecover(caller *frame) value { 595 // recover() must be exactly one level beneath the deferred 596 // function (two levels beneath the panicking function) to 597 // have any effect. Thus we ignore both "defer recover()" and 598 // "defer f() -> g() -> recover()". 599 if caller.i.mode&DisableRecover == 0 && 600 caller != nil && !caller.panicking && 601 caller.caller != nil && caller.caller.panicking { 602 caller.caller.panicking = false 603 p := caller.caller.panic 604 caller.caller.panic = nil 605 606 // TODO(adonovan): support runtime.Goexit. 607 switch p := p.(type) { 608 case targetPanic: 609 // The target program explicitly called panic(). 610 return p.v 611 case runtime.Error: 612 // The interpreter encountered a runtime error. 613 return iface{caller.i.runtimeErrorString, p.Error()} 614 case string: 615 // The interpreter explicitly called panic(). 616 return iface{caller.i.runtimeErrorString, p} 617 default: 618 panic(fmt.Sprintf("unexpected panic type %T in target call to recover()", p)) 619 } 620 } 621 return iface{} 622} 623 624// setGlobal sets the value of a system-initialized global variable. 625func setGlobal(i *interpreter, pkg *ssa.Package, name string, v value) { 626 if g, ok := i.globals[pkg.Var(name)]; ok { 627 *g = v 628 return 629 } 630 panic("no global variable: " + pkg.Pkg.Path() + "." + name) 631} 632 633var environ []value 634 635func init() { 636 for _, s := range os.Environ() { 637 environ = append(environ, s) 638 } 639 environ = append(environ, "GOSSAINTERP=1") 640 environ = append(environ, "GOARCH="+runtime.GOARCH) 641} 642 643// deleteBodies delete the bodies of all standalone functions except the 644// specified ones. A missing intrinsic leads to a clear runtime error. 645func deleteBodies(pkg *ssa.Package, except ...string) { 646 keep := make(map[string]bool) 647 for _, e := range except { 648 keep[e] = true 649 } 650 for _, mem := range pkg.Members { 651 if fn, ok := mem.(*ssa.Function); ok && !keep[fn.Name()] { 652 fn.Blocks = nil 653 } 654 } 655} 656 657// Interpret interprets the Go program whose main package is mainpkg. 658// mode specifies various interpreter options. filename and args are 659// the initial values of os.Args for the target program. sizes is the 660// effective type-sizing function for this program. 661// 662// Interpret returns the exit code of the program: 2 for panic (like 663// gc does), or the argument to os.Exit for normal termination. 664// 665// The SSA program must include the "runtime" package. 666// 667func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename string, args []string) (exitCode int) { 668 if syswrite == nil { 669 fmt.Fprintln(os.Stderr, "Interpret: unsupported platform.") 670 return 1 671 } 672 673 i := &interpreter{ 674 prog: mainpkg.Prog, 675 globals: make(map[ssa.Value]*value), 676 mode: mode, 677 sizes: sizes, 678 goroutines: 1, 679 } 680 runtimePkg := i.prog.ImportedPackage("runtime") 681 if runtimePkg == nil { 682 panic("ssa.Program doesn't include runtime package") 683 } 684 i.runtimeErrorString = runtimePkg.Type("errorString").Object().Type() 685 686 initReflect(i) 687 688 i.osArgs = append(i.osArgs, filename) 689 for _, arg := range args { 690 i.osArgs = append(i.osArgs, arg) 691 } 692 693 for _, pkg := range i.prog.AllPackages() { 694 // Initialize global storage. 695 for _, m := range pkg.Members { 696 switch v := m.(type) { 697 case *ssa.Global: 698 cell := zero(deref(v.Type())) 699 i.globals[v] = &cell 700 } 701 } 702 703 // Ad-hoc initialization for magic system variables. 704 switch pkg.Pkg.Path() { 705 case "syscall": 706 setGlobal(i, pkg, "envs", environ) 707 708 case "reflect": 709 deleteBodies(pkg, "DeepEqual", "deepValueEqual") 710 711 case "runtime": 712 sz := sizes.Sizeof(pkg.Pkg.Scope().Lookup("MemStats").Type()) 713 setGlobal(i, pkg, "sizeof_C_MStats", uintptr(sz)) 714 deleteBodies(pkg, "GOROOT", "gogetenv") 715 } 716 } 717 718 // Top-level error handler. 719 exitCode = 2 720 defer func() { 721 if exitCode != 2 || i.mode&DisableRecover != 0 { 722 return 723 } 724 switch p := recover().(type) { 725 case exitPanic: 726 exitCode = int(p) 727 return 728 case targetPanic: 729 fmt.Fprintln(os.Stderr, "panic:", toString(p.v)) 730 case runtime.Error: 731 fmt.Fprintln(os.Stderr, "panic:", p.Error()) 732 case string: 733 fmt.Fprintln(os.Stderr, "panic:", p) 734 default: 735 fmt.Fprintf(os.Stderr, "panic: unexpected type: %T: %v\n", p, p) 736 } 737 738 // TODO(adonovan): dump panicking interpreter goroutine? 739 // buf := make([]byte, 0x10000) 740 // runtime.Stack(buf, false) 741 // fmt.Fprintln(os.Stderr, string(buf)) 742 // (Or dump panicking target goroutine?) 743 }() 744 745 // Run! 746 call(i, nil, token.NoPos, mainpkg.Func("init"), nil) 747 if mainFn := mainpkg.Func("main"); mainFn != nil { 748 call(i, nil, token.NoPos, mainFn, nil) 749 exitCode = 0 750 } else { 751 fmt.Fprintln(os.Stderr, "No main function.") 752 exitCode = 1 753 } 754 return 755} 756 757// deref returns a pointer's element type; otherwise it returns typ. 758// TODO(adonovan): Import from ssa? 759func deref(typ types.Type) types.Type { 760 if p, ok := typ.Underlying().(*types.Pointer); ok { 761 return p.Elem() 762 } 763 return typ 764} 765