github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/compiler/symbol.go (about) 1 package compiler 2 3 // This file manages symbols, that is, functions and globals. It reads their 4 // pragmas, determines the link name, etc. 5 6 import ( 7 "fmt" 8 "go/ast" 9 "go/token" 10 "go/types" 11 "strconv" 12 "strings" 13 14 "github.com/tinygo-org/tinygo/compiler/llvmutil" 15 "github.com/tinygo-org/tinygo/loader" 16 "golang.org/x/tools/go/ssa" 17 "tinygo.org/x/go-llvm" 18 ) 19 20 // functionInfo contains some information about a function or method. In 21 // particular, it contains information obtained from pragmas. 22 // 23 // The linkName value contains a valid link name, even if //go:linkname is not 24 // present. 25 type functionInfo struct { 26 wasmModule string // go:wasm-module 27 wasmName string // wasm-export-name or wasm-import-name in the IR 28 linkName string // go:linkname, go:export - the IR function name 29 section string // go:section - object file section name 30 exported bool // go:export, CGo 31 interrupt bool // go:interrupt 32 nobounds bool // go:nobounds 33 variadic bool // go:variadic (CGo only) 34 inline inlineType // go:inline 35 } 36 37 type inlineType int 38 39 // How much to inline. 40 const ( 41 // Default behavior. The compiler decides for itself whether any given 42 // function will be inlined. Whether any function is inlined depends on the 43 // optimization level. 44 inlineDefault inlineType = iota 45 46 // Inline hint, just like the C inline keyword (signalled using 47 // //go:inline). The compiler will be more likely to inline this function, 48 // but it is not a guarantee. 49 inlineHint 50 51 // Don't inline, just like the GCC noinline attribute. Signalled using 52 // //go:noinline. 53 inlineNone 54 ) 55 56 // Values for the allockind attribute. Source: 57 // https://github.com/llvm/llvm-project/blob/release/16.x/llvm/include/llvm/IR/Attributes.h#L49 58 const ( 59 allocKindAlloc = 1 << iota 60 allocKindRealloc 61 allocKindFree 62 allocKindUninitialized 63 allocKindZeroed 64 allocKindAligned 65 ) 66 67 // getFunction returns the LLVM function for the given *ssa.Function, creating 68 // it if needed. It can later be filled with compilerContext.createFunction(). 69 func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value) { 70 info := c.getFunctionInfo(fn) 71 llvmFn := c.mod.NamedFunction(info.linkName) 72 if !llvmFn.IsNil() { 73 return llvmFn.GlobalValueType(), llvmFn 74 } 75 76 var retType llvm.Type 77 if fn.Signature.Results() == nil { 78 retType = c.ctx.VoidType() 79 } else if fn.Signature.Results().Len() == 1 { 80 retType = c.getLLVMType(fn.Signature.Results().At(0).Type()) 81 } else { 82 results := make([]llvm.Type, 0, fn.Signature.Results().Len()) 83 for i := 0; i < fn.Signature.Results().Len(); i++ { 84 results = append(results, c.getLLVMType(fn.Signature.Results().At(i).Type())) 85 } 86 retType = c.ctx.StructType(results, false) 87 } 88 89 var paramInfos []paramInfo 90 for _, param := range getParams(fn.Signature) { 91 paramType := c.getLLVMType(param.Type()) 92 paramFragmentInfos := c.expandFormalParamType(paramType, param.Name(), param.Type()) 93 paramInfos = append(paramInfos, paramFragmentInfos...) 94 } 95 96 // Add an extra parameter as the function context. This context is used in 97 // closures and bound methods, but should be optimized away when not used. 98 if !info.exported { 99 paramInfos = append(paramInfos, paramInfo{llvmType: c.dataPtrType, name: "context", elemSize: 0}) 100 } 101 102 var paramTypes []llvm.Type 103 for _, info := range paramInfos { 104 paramTypes = append(paramTypes, info.llvmType) 105 } 106 107 fnType := llvm.FunctionType(retType, paramTypes, info.variadic) 108 llvmFn = llvm.AddFunction(c.mod, info.linkName, fnType) 109 if strings.HasPrefix(c.Triple, "wasm") { 110 // C functions without prototypes like this: 111 // void foo(); 112 // are actually variadic functions. However, it appears that it has been 113 // decided in WebAssembly that such prototype-less functions are not 114 // allowed in WebAssembly. 115 // In C, this can only happen when there are zero parameters, hence this 116 // check here. For more information: 117 // https://reviews.llvm.org/D48443 118 // https://github.com/WebAssembly/tool-conventions/issues/16 119 if info.variadic && len(fn.Params) == 0 { 120 attr := c.ctx.CreateStringAttribute("no-prototype", "") 121 llvmFn.AddFunctionAttr(attr) 122 } 123 } 124 c.addStandardDeclaredAttributes(llvmFn) 125 126 dereferenceableOrNullKind := llvm.AttributeKindID("dereferenceable_or_null") 127 for i, info := range paramInfos { 128 if info.elemSize != 0 { 129 dereferenceableOrNull := c.ctx.CreateEnumAttribute(dereferenceableOrNullKind, info.elemSize) 130 llvmFn.AddAttributeAtIndex(i+1, dereferenceableOrNull) 131 } 132 } 133 134 // Set a number of function or parameter attributes, depending on the 135 // function. These functions are runtime functions that are known to have 136 // certain attributes that might not be inferred by the compiler. 137 switch info.linkName { 138 case "abort": 139 // On *nix systems, the "abort" functuion in libc is used to handle fatal panics. 140 // Mark it as noreturn so LLVM can optimize away code. 141 llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noreturn"), 0)) 142 case "runtime.alloc": 143 // Tell the optimizer that runtime.alloc is an allocator, meaning that it 144 // returns values that are never null and never alias to an existing value. 145 for _, attrName := range []string{"noalias", "nonnull"} { 146 llvmFn.AddAttributeAtIndex(0, c.ctx.CreateEnumAttribute(llvm.AttributeKindID(attrName), 0)) 147 } 148 // Add attributes to signal to LLVM that this is an allocator function. 149 // This enables a number of optimizations. 150 llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allockind"), allocKindAlloc|allocKindZeroed)) 151 llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("alloc-family", "runtime.alloc")) 152 // Use a special value to indicate the first parameter: 153 // > allocsize has two integer arguments, but because they're both 32 bits, we can 154 // > pack them into one 64-bit value, at the cost of making said value 155 // > nonsensical. 156 // > 157 // > In order to do this, we need to reserve one value of the second (optional) 158 // > allocsize argument to signify "not present." 159 llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("allocsize"), 0x0000_0000_ffff_ffff)) 160 case "runtime.sliceAppend": 161 // Appending a slice will only read the to-be-appended slice, it won't 162 // be modified. 163 llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) 164 llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)) 165 case "runtime.sliceCopy": 166 // Copying a slice won't capture any of the parameters. 167 llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("writeonly"), 0)) 168 llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) 169 llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)) 170 llvmFn.AddAttributeAtIndex(2, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) 171 case "runtime.trackPointer": 172 // This function is necessary for tracking pointers on the stack in a 173 // portable way (see gc_stack_portable.go). Indicate to the optimizer 174 // that the only thing we'll do is read the pointer. 175 llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) 176 llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)) 177 case "__mulsi3", "__divmodsi4", "__udivmodsi4": 178 if strings.Split(c.Triple, "-")[0] == "avr" { 179 // These functions are compiler-rt/libgcc functions that are 180 // currently implemented in Go. Assembly versions should appear in 181 // LLVM 17 hopefully. Until then, they need to be made available to 182 // the linker and the best way to do that is llvm.compiler.used. 183 // I considered adding a pragma for this, but the LLVM language 184 // reference explicitly says that this feature should not be exposed 185 // to source languages: 186 // > This is a rare construct that should only be used in rare 187 // > circumstances, and should not be exposed to source languages. 188 llvmutil.AppendToGlobal(c.mod, "llvm.compiler.used", llvmFn) 189 } 190 } 191 192 // External/exported functions may not retain pointer values. 193 // https://golang.org/cmd/cgo/#hdr-Passing_pointers 194 if info.exported { 195 if c.archFamily() == "wasm32" && len(fn.Blocks) == 0 { 196 // We need to add the wasm-import-module and the wasm-import-name 197 // attributes. 198 if info.wasmModule != "" { 199 llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-module", info.wasmModule)) 200 } 201 202 llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("wasm-import-name", info.wasmName)) 203 } 204 nocaptureKind := llvm.AttributeKindID("nocapture") 205 nocapture := c.ctx.CreateEnumAttribute(nocaptureKind, 0) 206 for i, typ := range paramTypes { 207 if typ.TypeKind() == llvm.PointerTypeKind { 208 llvmFn.AddAttributeAtIndex(i+1, nocapture) 209 } 210 } 211 } 212 213 // Synthetic functions are functions that do not appear in the source code, 214 // they are artificially constructed. Usually they are wrapper functions 215 // that are not referenced anywhere except in a SSA call instruction so 216 // should be created right away. 217 // The exception is the package initializer, which does appear in the 218 // *ssa.Package members and so shouldn't be created here. 219 if fn.Synthetic != "" && fn.Synthetic != "package initializer" && fn.Synthetic != "generic function" { 220 irbuilder := c.ctx.NewBuilder() 221 b := newBuilder(c, irbuilder, fn) 222 b.createFunction() 223 irbuilder.Dispose() 224 llvmFn.SetLinkage(llvm.LinkOnceODRLinkage) 225 llvmFn.SetUnnamedAddr(true) 226 } 227 228 return fnType, llvmFn 229 } 230 231 // getFunctionInfo returns information about a function that is not directly 232 // present in *ssa.Function, such as the link name and whether it should be 233 // exported. 234 func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo { 235 if info, ok := c.functionInfos[f]; ok { 236 return info 237 } 238 info := functionInfo{ 239 // Pick the default linkName. 240 linkName: f.RelString(nil), 241 } 242 // Check for //go: pragmas, which may change the link name (among others). 243 c.parsePragmas(&info, f) 244 c.functionInfos[f] = info 245 return info 246 } 247 248 // parsePragmas is used by getFunctionInfo to parse function pragmas such as 249 // //export or //go:noinline. 250 func (c *compilerContext) parsePragmas(info *functionInfo, f *ssa.Function) { 251 if f.Syntax() == nil { 252 return 253 } 254 if decl, ok := f.Syntax().(*ast.FuncDecl); ok && decl.Doc != nil { 255 for _, comment := range decl.Doc.List { 256 text := comment.Text 257 if strings.HasPrefix(text, "//export ") { 258 // Rewrite '//export' to '//go:export' for compatibility with 259 // gc. 260 text = "//go:" + text[2:] 261 } 262 if !strings.HasPrefix(text, "//go:") { 263 continue 264 } 265 parts := strings.Fields(text) 266 switch parts[0] { 267 case "//go:export": 268 if len(parts) != 2 { 269 continue 270 } 271 272 info.linkName = parts[1] 273 info.wasmName = info.linkName 274 info.exported = true 275 case "//go:interrupt": 276 if hasUnsafeImport(f.Pkg.Pkg) { 277 info.interrupt = true 278 } 279 case "//go:wasm-module": 280 // Alternative comment for setting the import module. 281 // This is deprecated, use //go:wasmimport instead. 282 if len(parts) != 2 { 283 continue 284 } 285 info.wasmModule = parts[1] 286 case "//go:wasmimport": 287 // Import a WebAssembly function, for example a WASI function. 288 // Original proposal: https://github.com/golang/go/issues/38248 289 // Allow globally: https://github.com/golang/go/issues/59149 290 if len(parts) != 3 { 291 continue 292 } 293 c.checkWasmImport(f, comment.Text) 294 info.exported = true 295 info.wasmModule = parts[1] 296 info.wasmName = parts[2] 297 case "//go:inline": 298 info.inline = inlineHint 299 case "//go:noinline": 300 info.inline = inlineNone 301 case "//go:linkname": 302 if len(parts) != 3 || parts[1] != f.Name() { 303 continue 304 } 305 // Only enable go:linkname when the package imports "unsafe". 306 // This is a slightly looser requirement than what gc uses: gc 307 // requires the file to import "unsafe", not the package as a 308 // whole. 309 if hasUnsafeImport(f.Pkg.Pkg) { 310 info.linkName = parts[2] 311 } 312 case "//go:section": 313 // Only enable go:section when the package imports "unsafe". 314 // go:section also implies go:noinline since inlining could 315 // move the code to a different section than that requested. 316 if len(parts) == 2 && hasUnsafeImport(f.Pkg.Pkg) { 317 info.section = parts[1] 318 info.inline = inlineNone 319 } 320 case "//go:nobounds": 321 // Skip bounds checking in this function. Useful for some 322 // runtime functions. 323 // This is somewhat dangerous and thus only imported in packages 324 // that import unsafe. 325 if hasUnsafeImport(f.Pkg.Pkg) { 326 info.nobounds = true 327 } 328 case "//go:variadic": 329 // The //go:variadic pragma is emitted by the CGo preprocessing 330 // pass for C variadic functions. This includes both explicit 331 // (with ...) and implicit (no parameters in signature) 332 // functions. 333 if strings.HasPrefix(f.Name(), "C.") { 334 // This prefix cannot naturally be created, it must have 335 // been created as a result of CGo preprocessing. 336 info.variadic = true 337 } 338 } 339 } 340 } 341 } 342 343 // Check whether this function cannot be used in //go:wasmimport. It will add an 344 // error if this is the case. 345 // 346 // The list of allowed types is based on this proposal: 347 // https://github.com/golang/go/issues/59149 348 func (c *compilerContext) checkWasmImport(f *ssa.Function, pragma string) { 349 if c.pkg.Path() == "runtime" || c.pkg.Path() == "syscall/js" { 350 // The runtime is a special case. Allow all kinds of parameters 351 // (importantly, including pointers). 352 return 353 } 354 if f.Blocks != nil { 355 // Defined functions cannot be exported. 356 c.addError(f.Pos(), fmt.Sprintf("can only use //go:wasmimport on declarations")) 357 return 358 } 359 if f.Signature.Results().Len() > 1 { 360 c.addError(f.Signature.Results().At(1).Pos(), fmt.Sprintf("%s: too many return values", pragma)) 361 } else if f.Signature.Results().Len() == 1 { 362 result := f.Signature.Results().At(0) 363 if !isValidWasmType(result.Type(), true) { 364 c.addError(result.Pos(), fmt.Sprintf("%s: unsupported result type %s", pragma, result.Type().String())) 365 } 366 } 367 for _, param := range f.Params { 368 // Check whether the type is allowed. 369 // Only a very limited number of types can be mapped to WebAssembly. 370 if !isValidWasmType(param.Type(), false) { 371 c.addError(param.Pos(), fmt.Sprintf("%s: unsupported parameter type %s", pragma, param.Type().String())) 372 } 373 } 374 } 375 376 // Check whether the type maps directly to a WebAssembly type, according to: 377 // https://github.com/golang/go/issues/59149 378 func isValidWasmType(typ types.Type, isReturn bool) bool { 379 switch typ := typ.Underlying().(type) { 380 case *types.Basic: 381 switch typ.Kind() { 382 case types.Int32, types.Uint32, types.Int64, types.Uint64: 383 return true 384 case types.Float32, types.Float64: 385 return true 386 case types.UnsafePointer: 387 if !isReturn { 388 return true 389 } 390 } 391 } 392 return false 393 } 394 395 // getParams returns the function parameters, including the receiver at the 396 // start. This is an alternative to the Params member of *ssa.Function, which is 397 // not yet populated when the package has not yet been built. 398 func getParams(sig *types.Signature) []*types.Var { 399 params := []*types.Var{} 400 if sig.Recv() != nil { 401 params = append(params, sig.Recv()) 402 } 403 for i := 0; i < sig.Params().Len(); i++ { 404 params = append(params, sig.Params().At(i)) 405 } 406 return params 407 } 408 409 // addStandardDeclaredAttributes adds attributes that are set for any function, 410 // whether declared or defined. 411 func (c *compilerContext) addStandardDeclaredAttributes(llvmFn llvm.Value) { 412 if c.SizeLevel >= 1 { 413 // Set the "optsize" attribute to make slightly smaller binaries at the 414 // cost of minimal performance loss (-Os in Clang). 415 kind := llvm.AttributeKindID("optsize") 416 attr := c.ctx.CreateEnumAttribute(kind, 0) 417 llvmFn.AddFunctionAttr(attr) 418 } 419 if c.SizeLevel >= 2 { 420 // Set the "minsize" attribute to reduce code size even further, 421 // regardless of performance loss (-Oz in Clang). 422 kind := llvm.AttributeKindID("minsize") 423 attr := c.ctx.CreateEnumAttribute(kind, 0) 424 llvmFn.AddFunctionAttr(attr) 425 } 426 if c.CPU != "" { 427 llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("target-cpu", c.CPU)) 428 } 429 if c.Features != "" { 430 llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("target-features", c.Features)) 431 } 432 } 433 434 // addStandardDefinedAttributes adds the set of attributes that are added to 435 // every function defined by TinyGo (even thunks/wrappers), possibly depending 436 // on the architecture. It does not set attributes only set for declared 437 // functions, use addStandardDeclaredAttributes for this. 438 func (c *compilerContext) addStandardDefinedAttributes(llvmFn llvm.Value) { 439 // TinyGo does not currently raise exceptions, so set the 'nounwind' flag. 440 // This behavior matches Clang when compiling C source files. 441 // It reduces binary size on Linux a little bit on non-x86_64 targets by 442 // eliminating exception tables for these functions. 443 llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0)) 444 if strings.Split(c.Triple, "-")[0] == "x86_64" { 445 // Required by the ABI. 446 // The uwtable has two possible values: sync (1) or async (2). We use 447 // sync because we currently don't use async unwind tables. 448 // For details, see: https://llvm.org/docs/LangRef.html#function-attributes 449 llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("uwtable"), 1)) 450 } 451 } 452 453 // addStandardAttributes adds all attributes added to defined functions. 454 func (c *compilerContext) addStandardAttributes(llvmFn llvm.Value) { 455 c.addStandardDeclaredAttributes(llvmFn) 456 c.addStandardDefinedAttributes(llvmFn) 457 } 458 459 // globalInfo contains some information about a specific global. By default, 460 // linkName is equal to .RelString(nil) on a global and extern is false, but for 461 // some symbols this is different (due to //go:extern for example). 462 type globalInfo struct { 463 linkName string // go:extern 464 extern bool // go:extern 465 align int // go:align 466 section string // go:section 467 } 468 469 // loadASTComments loads comments on globals from the AST, for use later in the 470 // program. In particular, they are required for //go:extern pragmas on globals. 471 func (c *compilerContext) loadASTComments(pkg *loader.Package) { 472 for _, file := range pkg.Files { 473 for _, decl := range file.Decls { 474 switch decl := decl.(type) { 475 case *ast.GenDecl: 476 switch decl.Tok { 477 case token.VAR: 478 if len(decl.Specs) != 1 { 479 continue 480 } 481 for _, spec := range decl.Specs { 482 switch spec := spec.(type) { 483 case *ast.ValueSpec: // decl.Tok == token.VAR 484 for _, name := range spec.Names { 485 id := pkg.Pkg.Path() + "." + name.Name 486 c.astComments[id] = decl.Doc 487 } 488 } 489 } 490 } 491 } 492 } 493 } 494 } 495 496 // getGlobal returns a LLVM IR global value for a Go SSA global. It is added to 497 // the LLVM IR if it has not been added already. 498 func (c *compilerContext) getGlobal(g *ssa.Global) llvm.Value { 499 info := c.getGlobalInfo(g) 500 llvmGlobal := c.mod.NamedGlobal(info.linkName) 501 if llvmGlobal.IsNil() { 502 typ := g.Type().(*types.Pointer).Elem() 503 llvmType := c.getLLVMType(typ) 504 llvmGlobal = llvm.AddGlobal(c.mod, llvmType, info.linkName) 505 506 // Set alignment from the //go:align comment. 507 alignment := c.targetData.ABITypeAlignment(llvmType) 508 if info.align > alignment { 509 alignment = info.align 510 } 511 if alignment <= 0 || alignment&(alignment-1) != 0 { 512 // Check for power-of-two (or 0). 513 // See: https://stackoverflow.com/a/108360 514 c.addError(g.Pos(), "global variable alignment must be a positive power of two") 515 } else { 516 // Set the alignment only when it is a power of two. 517 llvmGlobal.SetAlignment(alignment) 518 } 519 520 if c.Debug && !info.extern { 521 // Add debug info. 522 pos := c.program.Fset.Position(g.Pos()) 523 diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{ 524 Name: g.RelString(nil), 525 LinkageName: info.linkName, 526 File: c.getDIFile(pos.Filename), 527 Line: pos.Line, 528 Type: c.getDIType(typ), 529 LocalToUnit: false, 530 Expr: c.dibuilder.CreateExpression(nil), 531 AlignInBits: uint32(alignment) * 8, 532 }) 533 llvmGlobal.AddMetadata(0, diglobal) 534 } 535 } 536 return llvmGlobal 537 } 538 539 // getGlobalInfo returns some information about a specific global. 540 func (c *compilerContext) getGlobalInfo(g *ssa.Global) globalInfo { 541 info := globalInfo{ 542 // Pick the default linkName. 543 linkName: g.RelString(nil), 544 } 545 // Check for //go: pragmas, which may change the link name (among others). 546 doc := c.astComments[info.linkName] 547 if doc != nil { 548 info.parsePragmas(doc) 549 } 550 return info 551 } 552 553 // Parse //go: pragma comments from the source. In particular, it parses the 554 // //go:extern pragma on globals. 555 func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) { 556 for _, comment := range doc.List { 557 if !strings.HasPrefix(comment.Text, "//go:") { 558 continue 559 } 560 parts := strings.Fields(comment.Text) 561 switch parts[0] { 562 case "//go:extern": 563 info.extern = true 564 if len(parts) == 2 { 565 info.linkName = parts[1] 566 } 567 case "//go:align": 568 align, err := strconv.Atoi(parts[1]) 569 if err == nil { 570 info.align = align 571 } 572 case "//go:section": 573 if len(parts) == 2 { 574 info.section = parts[1] 575 } 576 } 577 } 578 } 579 580 // Get all methods of a type. 581 func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection { 582 ms := prog.MethodSets.MethodSet(typ) 583 methods := make([]*types.Selection, ms.Len()) 584 for i := 0; i < ms.Len(); i++ { 585 methods[i] = ms.At(i) 586 } 587 return methods 588 } 589 590 // Return true if this package imports "unsafe", false otherwise. 591 func hasUnsafeImport(pkg *types.Package) bool { 592 for _, imp := range pkg.Imports() { 593 if imp == types.Unsafe { 594 return true 595 } 596 } 597 return false 598 }