PR tree-optimization/86274 - SEGFAULT when logging std::to_string(NAN)
[official-gcc.git] / libgo / go / cmd / vet / cgo.go
blob76364ff6ed8bec5cf26c143828f9cc1717780ff8
1 // Copyright 2015 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.
5 // Check for invalid cgo pointer passing.
6 // This looks for code that uses cgo to call C code passing values
7 // whose types are almost always invalid according to the cgo pointer
8 // sharing rules.
9 // Specifically, it warns about attempts to pass a Go chan, map, func,
10 // or slice to C, either directly, or via a pointer, array, or struct.
12 package main
14 import (
15 "go/ast"
16 "go/token"
17 "go/types"
20 func init() {
21 register("cgocall",
22 "check for types that may not be passed to cgo calls",
23 checkCgoCall,
24 callExpr)
27 func checkCgoCall(f *File, node ast.Node) {
28 x := node.(*ast.CallExpr)
30 // We are only looking for calls to functions imported from
31 // the "C" package.
32 sel, ok := x.Fun.(*ast.SelectorExpr)
33 if !ok {
34 return
36 id, ok := sel.X.(*ast.Ident)
37 if !ok {
38 return
41 pkgname, ok := f.pkg.uses[id].(*types.PkgName)
42 if !ok || pkgname.Imported().Path() != "C" {
43 return
46 // A call to C.CBytes passes a pointer but is always safe.
47 if sel.Sel.Name == "CBytes" {
48 return
51 for _, arg := range x.Args {
52 if !typeOKForCgoCall(cgoBaseType(f, arg), make(map[types.Type]bool)) {
53 f.Badf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
56 // Check for passing the address of a bad type.
57 if conv, ok := arg.(*ast.CallExpr); ok && len(conv.Args) == 1 && f.hasBasicType(conv.Fun, types.UnsafePointer) {
58 arg = conv.Args[0]
60 if u, ok := arg.(*ast.UnaryExpr); ok && u.Op == token.AND {
61 if !typeOKForCgoCall(cgoBaseType(f, u.X), make(map[types.Type]bool)) {
62 f.Badf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
68 // cgoBaseType tries to look through type conversions involving
69 // unsafe.Pointer to find the real type. It converts:
70 // unsafe.Pointer(x) => x
71 // *(*unsafe.Pointer)(unsafe.Pointer(&x)) => x
72 func cgoBaseType(f *File, arg ast.Expr) types.Type {
73 switch arg := arg.(type) {
74 case *ast.CallExpr:
75 if len(arg.Args) == 1 && f.hasBasicType(arg.Fun, types.UnsafePointer) {
76 return cgoBaseType(f, arg.Args[0])
78 case *ast.StarExpr:
79 call, ok := arg.X.(*ast.CallExpr)
80 if !ok || len(call.Args) != 1 {
81 break
83 // Here arg is *f(v).
84 t := f.pkg.types[call.Fun].Type
85 if t == nil {
86 break
88 ptr, ok := t.Underlying().(*types.Pointer)
89 if !ok {
90 break
92 // Here arg is *(*p)(v)
93 elem, ok := ptr.Elem().Underlying().(*types.Basic)
94 if !ok || elem.Kind() != types.UnsafePointer {
95 break
97 // Here arg is *(*unsafe.Pointer)(v)
98 call, ok = call.Args[0].(*ast.CallExpr)
99 if !ok || len(call.Args) != 1 {
100 break
102 // Here arg is *(*unsafe.Pointer)(f(v))
103 if !f.hasBasicType(call.Fun, types.UnsafePointer) {
104 break
106 // Here arg is *(*unsafe.Pointer)(unsafe.Pointer(v))
107 u, ok := call.Args[0].(*ast.UnaryExpr)
108 if !ok || u.Op != token.AND {
109 break
111 // Here arg is *(*unsafe.Pointer)(unsafe.Pointer(&v))
112 return cgoBaseType(f, u.X)
115 return f.pkg.types[arg].Type
118 // typeOKForCgoCall reports whether the type of arg is OK to pass to a
119 // C function using cgo. This is not true for Go types with embedded
120 // pointers. m is used to avoid infinite recursion on recursive types.
121 func typeOKForCgoCall(t types.Type, m map[types.Type]bool) bool {
122 if t == nil || m[t] {
123 return true
125 m[t] = true
126 switch t := t.Underlying().(type) {
127 case *types.Chan, *types.Map, *types.Signature, *types.Slice:
128 return false
129 case *types.Pointer:
130 return typeOKForCgoCall(t.Elem(), m)
131 case *types.Array:
132 return typeOKForCgoCall(t.Elem(), m)
133 case *types.Struct:
134 for i := 0; i < t.NumFields(); i++ {
135 if !typeOKForCgoCall(t.Field(i).Type(), m) {
136 return false
140 return true