Commit 6d435870 authored by Ian Lance Taylor's avatar Ian Lance Taylor

cmd/cgo: preserve type information across loadDWARF loop

CL 122575 and its successors introduced a loop calling loadDWARF,
whereas before we only called it once. Pass a single typeConv to each
call, rather than creating a new one in loadDWARF itself. Change the
maps from dwarf.Type to use string keys rather than dwarf.Type keys,
since when the DWARF is reloaded the dwarf.Type pointers will be
different. These changes permit typeConv.Type to return a consistent
value for a given DWARF type, avoiding spurious type conversion errors
due to typedefs loaded after the first loop iteration.

Fixes #27340

Change-Id: Ic33467bbfca4c54e95909621b35ba2a58216d96e
Reviewed-on: https://go-review.googlesource.com/c/152762
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarKeith Randall <khr@golang.org>
parent 1ac3b061
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Failed to resolve typedefs consistently.
// No runtime test; just make sure it compiles.
package cgotest
import "./issue27340"
var issue27340Var = issue27340.Issue27340GoFunc
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Failed to resolve typedefs consistently.
// No runtime test; just make sure it compiles.
// In separate directory to isolate #pragma GCC diagnostic.
package issue27340
// We use the #pragma to avoid a compiler warning about incompatible
// pointer types, because we generate code passing a struct ptr rather
// than using the typedef. This warning is expected and does not break
// a normal build.
// We can only disable -Wincompatible-pointer-types starting with GCC 5.
// #if __GNU_MAJOR__ >= 5
//
// #pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
//
// typedef struct {
// int a;
// } issue27340Struct, *issue27340Ptr;
//
// static void issue27340CFunc(issue27340Ptr p) {}
//
// #else /* _GNU_MAJOR_ < 5 */
//
// typedef struct {
// int a;
// } issue27340Struct;
//
// static issue27340Struct* issue27340Ptr(issue27340Struct* p) { return p; }
//
// static void issue27340CFunc(issue27340Struct *p) {}
// #endif /* _GNU_MAJOR_ < 5 */
import "C"
func Issue27340GoFunc() {
var s C.issue27340Struct
C.issue27340CFunc(C.issue27340Ptr(&s))
}
...@@ -29,7 +29,7 @@ func Test(t *testing.T) { ...@@ -29,7 +29,7 @@ func Test(t *testing.T) {
// Brittle: the assertion may fail spuriously when the algorithm // Brittle: the assertion may fail spuriously when the algorithm
// changes, but should remain stable otherwise. // changes, but should remain stable otherwise.
got := fmt.Sprintf("%T %T", in, opts) got := fmt.Sprintf("%T %T", in, opts)
want := "issue9026._Ctype_struct___0 *issue9026._Ctype_struct___1" want := "issue9026._Ctype_struct___0 *issue9026._Ctype_struct___0"
if got != want { if got != want {
t.Errorf("Non-deterministic type names: got %s, want %s", got, want) t.Errorf("Non-deterministic type names: got %s, want %s", got, want)
} }
......
...@@ -170,6 +170,10 @@ func (p *Package) Translate(f *File) { ...@@ -170,6 +170,10 @@ func (p *Package) Translate(f *File) {
// Convert C.ulong to C.unsigned long, etc. // Convert C.ulong to C.unsigned long, etc.
cref.Name.C = cname(cref.Name.Go) cref.Name.C = cname(cref.Name.Go)
} }
var conv typeConv
conv.Init(p.PtrSize, p.IntSize)
p.loadDefines(f) p.loadDefines(f)
p.typedefs = map[string]bool{} p.typedefs = map[string]bool{}
p.typedefList = nil p.typedefList = nil
...@@ -187,7 +191,7 @@ func (p *Package) Translate(f *File) { ...@@ -187,7 +191,7 @@ func (p *Package) Translate(f *File) {
} }
needType := p.guessKinds(f) needType := p.guessKinds(f)
if len(needType) > 0 { if len(needType) > 0 {
p.loadDWARF(f, needType) p.loadDWARF(f, &conv, needType)
} }
// In godefs mode we're OK with the typedefs, which // In godefs mode we're OK with the typedefs, which
...@@ -482,7 +486,7 @@ func (p *Package) guessKinds(f *File) []*Name { ...@@ -482,7 +486,7 @@ func (p *Package) guessKinds(f *File) []*Name {
// loadDWARF parses the DWARF debug information generated // loadDWARF parses the DWARF debug information generated
// by gcc to learn the details of the constants, variables, and types // by gcc to learn the details of the constants, variables, and types
// being referred to as C.xxx. // being referred to as C.xxx.
func (p *Package) loadDWARF(f *File, names []*Name) { func (p *Package) loadDWARF(f *File, conv *typeConv, names []*Name) {
// Extract the types from the DWARF section of an object // Extract the types from the DWARF section of an object
// from a well-formed C program. Gcc only generates DWARF info // from a well-formed C program. Gcc only generates DWARF info
// for symbols in the object file, so it is not enough to print the // for symbols in the object file, so it is not enough to print the
...@@ -589,8 +593,6 @@ func (p *Package) loadDWARF(f *File, names []*Name) { ...@@ -589,8 +593,6 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
} }
// Record types and typedef information. // Record types and typedef information.
var conv typeConv
conv.Init(p.PtrSize, p.IntSize)
for i, n := range names { for i, n := range names {
if strings.HasSuffix(n.Go, "GetTypeID") && types[i].String() == "func() CFTypeID" { if strings.HasSuffix(n.Go, "GetTypeID") && types[i].String() == "func() CFTypeID" {
conv.getTypeIDs[n.Go[:len(n.Go)-9]] = true conv.getTypeIDs[n.Go[:len(n.Go)-9]] = true
...@@ -2011,10 +2013,10 @@ func runGcc(stdin []byte, args []string) (string, string) { ...@@ -2011,10 +2013,10 @@ func runGcc(stdin []byte, args []string) (string, string) {
// with equivalent memory layout. // with equivalent memory layout.
type typeConv struct { type typeConv struct {
// Cache of already-translated or in-progress types. // Cache of already-translated or in-progress types.
m map[dwarf.Type]*Type m map[string]*Type
// Map from types to incomplete pointers to those types. // Map from types to incomplete pointers to those types.
ptrs map[dwarf.Type][]*Type ptrs map[string][]*Type
// Keys of ptrs in insertion order (deterministic worklist) // Keys of ptrs in insertion order (deterministic worklist)
// ptrKeys contains exactly the keys in ptrs. // ptrKeys contains exactly the keys in ptrs.
ptrKeys []dwarf.Type ptrKeys []dwarf.Type
...@@ -2049,8 +2051,8 @@ var unionWithPointer = make(map[ast.Expr]bool) ...@@ -2049,8 +2051,8 @@ var unionWithPointer = make(map[ast.Expr]bool)
func (c *typeConv) Init(ptrSize, intSize int64) { func (c *typeConv) Init(ptrSize, intSize int64) {
c.ptrSize = ptrSize c.ptrSize = ptrSize
c.intSize = intSize c.intSize = intSize
c.m = make(map[dwarf.Type]*Type) c.m = make(map[string]*Type)
c.ptrs = make(map[dwarf.Type][]*Type) c.ptrs = make(map[string][]*Type)
c.getTypeIDs = make(map[string]bool) c.getTypeIDs = make(map[string]bool)
c.bool = c.Ident("bool") c.bool = c.Ident("bool")
c.byte = c.Ident("byte") c.byte = c.Ident("byte")
...@@ -2158,11 +2160,12 @@ func (c *typeConv) FinishType(pos token.Pos) { ...@@ -2158,11 +2160,12 @@ func (c *typeConv) FinishType(pos token.Pos) {
// Keep looping until they're all done. // Keep looping until they're all done.
for len(c.ptrKeys) > 0 { for len(c.ptrKeys) > 0 {
dtype := c.ptrKeys[0] dtype := c.ptrKeys[0]
dtypeKey := dtype.String()
c.ptrKeys = c.ptrKeys[1:] c.ptrKeys = c.ptrKeys[1:]
ptrs := c.ptrs[dtype] ptrs := c.ptrs[dtypeKey]
delete(c.ptrs, dtype) delete(c.ptrs, dtypeKey)
// Note Type might invalidate c.ptrs[dtype]. // Note Type might invalidate c.ptrs[dtypeKey].
t := c.Type(dtype, pos) t := c.Type(dtype, pos)
for _, ptr := range ptrs { for _, ptr := range ptrs {
ptr.Go.(*ast.StarExpr).X = t.Go ptr.Go.(*ast.StarExpr).X = t.Go
...@@ -2174,7 +2177,8 @@ func (c *typeConv) FinishType(pos token.Pos) { ...@@ -2174,7 +2177,8 @@ func (c *typeConv) FinishType(pos token.Pos) {
// Type returns a *Type with the same memory layout as // Type returns a *Type with the same memory layout as
// dtype when used as the type of a variable or a struct field. // dtype when used as the type of a variable or a struct field.
func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
if t, ok := c.m[dtype]; ok { key := dtype.String()
if t, ok := c.m[key]; ok {
if t.Go == nil { if t.Go == nil {
fatalf("%s: type conversion loop at %s", lineno(pos), dtype) fatalf("%s: type conversion loop at %s", lineno(pos), dtype)
} }
...@@ -2185,7 +2189,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { ...@@ -2185,7 +2189,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
t.Size = dtype.Size() // note: wrong for array of pointers, corrected below t.Size = dtype.Size() // note: wrong for array of pointers, corrected below
t.Align = -1 t.Align = -1
t.C = &TypeRepr{Repr: dtype.Common().Name} t.C = &TypeRepr{Repr: dtype.Common().Name}
c.m[dtype] = t c.m[key] = t
switch dt := dtype.(type) { switch dt := dtype.(type) {
default: default:
...@@ -2348,10 +2352,11 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { ...@@ -2348,10 +2352,11 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
// Placeholder initialization; completed in FinishType. // Placeholder initialization; completed in FinishType.
t.Go = &ast.StarExpr{} t.Go = &ast.StarExpr{}
t.C.Set("<incomplete>*") t.C.Set("<incomplete>*")
if _, ok := c.ptrs[dt.Type]; !ok { key := dt.Type.String()
if _, ok := c.ptrs[key]; !ok {
c.ptrKeys = append(c.ptrKeys, dt.Type) c.ptrKeys = append(c.ptrKeys, dt.Type)
} }
c.ptrs[dt.Type] = append(c.ptrs[dt.Type], t) c.ptrs[key] = append(c.ptrs[key], t)
case *dwarf.QualType: case *dwarf.QualType:
t1 := c.Type(dt.Type, pos) t1 := c.Type(dt.Type, pos)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment