diff options
author | Ian Lance Taylor <iant@google.com> | 2016-02-03 21:58:02 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2016-02-03 21:58:02 +0000 |
commit | f98dd1a338867a408f7c72d73fbad7fe7fc93e3a (patch) | |
tree | 2f8da9862a9c1fe0df138917f997b03439c02773 /libgo/go/cmd/cgo/gcc.go | |
parent | b081ed4efc144da0c45a6484aebfd10e0eb9fda3 (diff) | |
download | gcc-f98dd1a338867a408f7c72d73fbad7fe7fc93e3a.tar.gz |
libgo: Update to go1.6rc1.
Reviewed-on: https://go-review.googlesource.com/19200
From-SVN: r233110
Diffstat (limited to 'libgo/go/cmd/cgo/gcc.go')
-rw-r--r-- | libgo/go/cmd/cgo/gcc.go | 354 |
1 files changed, 349 insertions, 5 deletions
diff --git a/libgo/go/cmd/cgo/gcc.go b/libgo/go/cmd/cgo/gcc.go index e0b89ec14cb..fb5049c1a1d 100644 --- a/libgo/go/cmd/cgo/gcc.go +++ b/libgo/go/cmd/cgo/gcc.go @@ -38,8 +38,8 @@ var nameToC = map[string]string{ "ulong": "unsigned long", "longlong": "long long", "ulonglong": "unsigned long long", - "complexfloat": "float complex", - "complexdouble": "double complex", + "complexfloat": "float _Complex", + "complexdouble": "double _Complex", } // cname returns the C name to use for C.s. @@ -167,6 +167,7 @@ func (p *Package) Translate(f *File) { if len(needType) > 0 { p.loadDWARF(f, needType) } + p.rewriteCalls(f) p.rewriteRef(f) } @@ -575,6 +576,331 @@ func (p *Package) mangleName(n *Name) { n.Mangle = prefix + n.Kind + "_" + n.Go } +// rewriteCalls rewrites all calls that pass pointers to check that +// they follow the rules for passing pointers between Go and C. +func (p *Package) rewriteCalls(f *File) { + for _, call := range f.Calls { + // This is a call to C.xxx; set goname to "xxx". + goname := call.Fun.(*ast.SelectorExpr).Sel.Name + if goname == "malloc" { + continue + } + name := f.Name[goname] + if name.Kind != "func" { + // Probably a type conversion. + continue + } + p.rewriteCall(f, call, name) + } +} + +// rewriteCall rewrites one call to add pointer checks. We replace +// each pointer argument x with _cgoCheckPointer(x).(T). +func (p *Package) rewriteCall(f *File, call *ast.CallExpr, name *Name) { + for i, param := range name.FuncType.Params { + if len(call.Args) <= i { + // Avoid a crash; this will be caught when the + // generated file is compiled. + return + } + + // An untyped nil does not need a pointer check, and + // when _cgoCheckPointer returns the untyped nil the + // type assertion we are going to insert will fail. + // Easier to just skip nil arguments. + // TODO: Note that this fails if nil is shadowed. + if id, ok := call.Args[i].(*ast.Ident); ok && id.Name == "nil" { + continue + } + + if !p.needsPointerCheck(f, param.Go) { + continue + } + + c := &ast.CallExpr{ + Fun: ast.NewIdent("_cgoCheckPointer"), + Args: []ast.Expr{ + call.Args[i], + }, + } + + // Add optional additional arguments for an address + // expression. + c.Args = p.checkAddrArgs(f, c.Args, call.Args[i]) + + // _cgoCheckPointer returns interface{}. + // We need to type assert that to the type we want. + // If the Go version of this C type uses + // unsafe.Pointer, we can't use a type assertion, + // because the Go file might not import unsafe. + // Instead we use a local variant of _cgoCheckPointer. + + var arg ast.Expr + if n := p.unsafeCheckPointerName(param.Go); n != "" { + c.Fun = ast.NewIdent(n) + arg = c + } else { + // In order for the type assertion to succeed, + // we need it to match the actual type of the + // argument. The only type we have is the + // type of the function parameter. We know + // that the argument type must be assignable + // to the function parameter type, or the code + // would not compile, but there is nothing + // requiring that the types be exactly the + // same. Add a type conversion to the + // argument so that the type assertion will + // succeed. + c.Args[0] = &ast.CallExpr{ + Fun: param.Go, + Args: []ast.Expr{ + c.Args[0], + }, + } + + arg = &ast.TypeAssertExpr{ + X: c, + Type: param.Go, + } + } + + call.Args[i] = arg + } +} + +// needsPointerCheck returns whether the type t needs a pointer check. +// This is true if t is a pointer and if the value to which it points +// might contain a pointer. +func (p *Package) needsPointerCheck(f *File, t ast.Expr) bool { + return p.hasPointer(f, t, true) +} + +// hasPointer is used by needsPointerCheck. If top is true it returns +// whether t is or contains a pointer that might point to a pointer. +// If top is false it returns whether t is or contains a pointer. +// f may be nil. +func (p *Package) hasPointer(f *File, t ast.Expr, top bool) bool { + switch t := t.(type) { + case *ast.ArrayType: + if t.Len == nil { + if !top { + return true + } + return p.hasPointer(f, t.Elt, false) + } + return p.hasPointer(f, t.Elt, top) + case *ast.StructType: + for _, field := range t.Fields.List { + if p.hasPointer(f, field.Type, top) { + return true + } + } + return false + case *ast.StarExpr: // Pointer type. + if !top { + return true + } + return p.hasPointer(f, t.X, false) + case *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType: + return true + case *ast.Ident: + // TODO: Handle types defined within function. + for _, d := range p.Decl { + gd, ok := d.(*ast.GenDecl) + if !ok || gd.Tok != token.TYPE { + continue + } + for _, spec := range gd.Specs { + ts, ok := spec.(*ast.TypeSpec) + if !ok { + continue + } + if ts.Name.Name == t.Name { + return p.hasPointer(f, ts.Type, top) + } + } + } + if def := typedef[t.Name]; def != nil { + return p.hasPointer(f, def.Go, top) + } + if t.Name == "string" { + return !top + } + if t.Name == "error" { + return true + } + if goTypes[t.Name] != nil { + return false + } + // We can't figure out the type. Conservative + // approach is to assume it has a pointer. + return true + case *ast.SelectorExpr: + if l, ok := t.X.(*ast.Ident); !ok || l.Name != "C" { + // Type defined in a different package. + // Conservative approach is to assume it has a + // pointer. + return true + } + if f == nil { + // Conservative approach: assume pointer. + return true + } + name := f.Name[t.Sel.Name] + if name != nil && name.Kind == "type" && name.Type != nil && name.Type.Go != nil { + return p.hasPointer(f, name.Type.Go, top) + } + // We can't figure out the type. Conservative + // approach is to assume it has a pointer. + return true + default: + error_(t.Pos(), "could not understand type %s", gofmt(t)) + return true + } +} + +// checkAddrArgs tries to add arguments to the call of +// _cgoCheckPointer when the argument is an address expression. We +// pass true to mean that the argument is an address operation of +// something other than a slice index, which means that it's only +// necessary to check the specific element pointed to, not the entire +// object. This is for &s.f, where f is a field in a struct. We can +// pass a slice or array, meaning that we should check the entire +// slice or array but need not check any other part of the object. +// This is for &s.a[i], where we need to check all of a. However, we +// only pass the slice or array if we can refer to it without side +// effects. +func (p *Package) checkAddrArgs(f *File, args []ast.Expr, x ast.Expr) []ast.Expr { + // Strip type conversions. + for { + c, ok := x.(*ast.CallExpr) + if !ok || len(c.Args) != 1 || !p.isType(c.Fun) { + break + } + x = c.Args[0] + } + u, ok := x.(*ast.UnaryExpr) + if !ok || u.Op != token.AND { + return args + } + index, ok := u.X.(*ast.IndexExpr) + if !ok { + // This is the address of something that is not an + // index expression. We only need to examine the + // single value to which it points. + // TODO: what if true is shadowed? + return append(args, ast.NewIdent("true")) + } + if !p.hasSideEffects(f, index.X) { + // Examine the entire slice. + return append(args, index.X) + } + // Treat the pointer as unknown. + return args +} + +// hasSideEffects returns whether the expression x has any side +// effects. x is an expression, not a statement, so the only side +// effect is a function call. +func (p *Package) hasSideEffects(f *File, x ast.Expr) bool { + found := false + f.walk(x, "expr", + func(f *File, x interface{}, context string) { + switch x.(type) { + case *ast.CallExpr: + found = true + } + }) + return found +} + +// isType returns whether the expression is definitely a type. +// This is conservative--it returns false for an unknown identifier. +func (p *Package) isType(t ast.Expr) bool { + switch t := t.(type) { + case *ast.SelectorExpr: + if t.Sel.Name != "Pointer" { + return false + } + id, ok := t.X.(*ast.Ident) + if !ok { + return false + } + return id.Name == "unsafe" + case *ast.Ident: + // TODO: This ignores shadowing. + switch t.Name { + case "unsafe.Pointer", "bool", "byte", + "complex64", "complex128", + "error", + "float32", "float64", + "int", "int8", "int16", "int32", "int64", + "rune", "string", + "uint", "uint8", "uint16", "uint32", "uint64", "uintptr": + + return true + } + case *ast.StarExpr: + return p.isType(t.X) + case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, + *ast.MapType, *ast.ChanType: + + return true + } + return false +} + +// unsafeCheckPointerName is given the Go version of a C type. If the +// type uses unsafe.Pointer, we arrange to build a version of +// _cgoCheckPointer that returns that type. This avoids using a type +// assertion to unsafe.Pointer in our copy of user code. We return +// the name of the _cgoCheckPointer function we are going to build, or +// the empty string if the type does not use unsafe.Pointer. +func (p *Package) unsafeCheckPointerName(t ast.Expr) string { + if !p.hasUnsafePointer(t) { + return "" + } + var buf bytes.Buffer + conf.Fprint(&buf, fset, t) + s := buf.String() + for i, t := range p.CgoChecks { + if s == t { + return p.unsafeCheckPointerNameIndex(i) + } + } + p.CgoChecks = append(p.CgoChecks, s) + return p.unsafeCheckPointerNameIndex(len(p.CgoChecks) - 1) +} + +// hasUnsafePointer returns whether the Go type t uses unsafe.Pointer. +// t is the Go version of a C type, so we don't need to handle every case. +// We only care about direct references, not references via typedefs. +func (p *Package) hasUnsafePointer(t ast.Expr) bool { + switch t := t.(type) { + case *ast.Ident: + // We don't see a SelectorExpr for unsafe.Pointer; + // this is created by code in this file. + return t.Name == "unsafe.Pointer" + case *ast.ArrayType: + return p.hasUnsafePointer(t.Elt) + case *ast.StructType: + for _, f := range t.Fields.List { + if p.hasUnsafePointer(f.Type) { + return true + } + } + case *ast.StarExpr: // Pointer type. + return p.hasUnsafePointer(t.X) + } + return false +} + +// unsafeCheckPointerNameIndex returns the name to use for a +// _cgoCheckPointer variant based on the index in the CgoChecks slice. +func (p *Package) unsafeCheckPointerNameIndex(i int) string { + return fmt.Sprintf("_cgoCheckPointer%d", i) +} + // rewriteRef rewrites all the C.xxx references in f.AST to refer to the // Go equivalents, now that we have figured out the meaning of all // the xxx. In *godefs mode, rewriteRef replaces the names @@ -612,6 +938,10 @@ func (p *Package) rewriteRef(f *File) { if r.Name.Kind != "func" { if r.Name.Kind == "type" { r.Context = "type" + if r.Name.Type == nil { + error_(r.Pos(), "invalid conversion to C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C) + break + } expr = r.Name.Type.Go break } @@ -663,6 +993,10 @@ func (p *Package) rewriteRef(f *File) { } } else if r.Name.Kind == "type" { // Okay - might be new(T) + if r.Name.Type == nil { + error_(r.Pos(), "expression C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C) + break + } expr = r.Name.Type.Go } else if r.Name.Kind == "var" { expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr} @@ -1028,12 +1362,12 @@ var dwarfToName = map[string]string{ "long unsigned int": "ulong", "unsigned int": "uint", "short unsigned int": "ushort", + "unsigned short": "ushort", // Used by Clang; issue 13129. "short int": "short", "long long int": "longlong", "long long unsigned int": "ulonglong", "signed char": "schar", - "float complex": "complexfloat", - "double complex": "complexdouble", + "unsigned char": "uchar", } const signedDelta = 64 @@ -1224,6 +1558,11 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { t.Go = c.int32 case 8: t.Go = c.int64 + case 16: + t.Go = &ast.ArrayType{ + Len: c.intExpr(t.Size), + Elt: c.uint8, + } } if t.Align = t.Size; t.Align >= c.ptrSize { t.Align = c.ptrSize @@ -1381,6 +1720,11 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { t.Go = c.uint32 case 8: t.Go = c.uint64 + case 16: + t.Go = &ast.ArrayType{ + Len: c.intExpr(t.Size), + Elt: c.uint8, + } } if t.Align = t.Size; t.Align >= c.ptrSize { t.Align = c.ptrSize @@ -1393,7 +1737,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { } switch dtype.(type) { - case *dwarf.AddrType, *dwarf.BoolType, *dwarf.CharType, *dwarf.IntType, *dwarf.FloatType, *dwarf.UcharType, *dwarf.UintType: + case *dwarf.AddrType, *dwarf.BoolType, *dwarf.CharType, *dwarf.ComplexType, *dwarf.IntType, *dwarf.FloatType, *dwarf.UcharType, *dwarf.UintType: s := dtype.Common().Name if s != "" { if ss, ok := dwarfToName[s]; ok { |