cmd/cgo: don't translate bitfields into Go fields

The cgo tool would sometimes emit a bitfield at an offset that did not
correspond to the C offset, such as for the example in the new test.

Change-Id: I61b2ca10ee44a42f81c13ed12865f2060168fed5
Reviewed-on: https://go-review.googlesource.com/c/go/+/252378
Trust: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
diff --git a/doc/go1.16.html b/doc/go1.16.html
index f177226..0167030 100644
--- a/doc/go1.16.html
+++ b/doc/go1.16.html
@@ -86,6 +86,16 @@
   by <code>go</code> <code>mod</code> <code>vendor</code> since Go 1.11.
 </p>
 
+<h3 id="cgo">Cgo</h3>
+
+<p> <!-- CL 252378 -->
+  The <a href="/cmd/cgo">cgo</a> tool will no longer try to translate
+  C struct bitfields into Go struct fields, even if their size can be
+  represented in Go. The order in which C bitfields appear in memory
+  is implementation dependent, so in some cases the cgo tool produced
+  results that were silently incorrect.
+</p>
+
 <p>
   TODO
 </p>
diff --git a/misc/cgo/testgodefs/testdata/bitfields.go b/misc/cgo/testgodefs/testdata/bitfields.go
new file mode 100644
index 0000000..6a9724d
--- /dev/null
+++ b/misc/cgo/testgodefs/testdata/bitfields.go
@@ -0,0 +1,31 @@
+// Copyright 2020 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.
+//
+// +build ignore
+
+package main
+
+// This file tests that we don't generate an incorrect field location
+// for a bitfield that appears aligned.
+
+/*
+struct bitfields {
+    unsigned int B1     :  5;
+    unsigned int B2     :  1;
+    unsigned int B3     :  1;
+    unsigned int B4     :  1;
+    unsigned int Short1 : 16; // misaligned on 8 bit boundary
+    unsigned int B5     :  1;
+    unsigned int B6     :  1;
+    unsigned int B7     :  1;
+    unsigned int B8     :  1;
+    unsigned int B9     :  1;
+    unsigned int B10    :  3;
+    unsigned int Short2 : 16; // alignment is OK
+    unsigned int Short3 : 16; // alignment is OK
+};
+*/
+import "C"
+
+type bitfields C.struct_bitfields
diff --git a/misc/cgo/testgodefs/testdata/main.go b/misc/cgo/testgodefs/testdata/main.go
index 2e1ad33..4a3f6a7 100644
--- a/misc/cgo/testgodefs/testdata/main.go
+++ b/misc/cgo/testgodefs/testdata/main.go
@@ -4,6 +4,12 @@
 
 package main
 
+import (
+	"fmt"
+	"os"
+	"reflect"
+)
+
 // Test that the struct field in anonunion.go was promoted.
 var v1 T
 var v2 = v1.L
@@ -23,4 +29,26 @@
 var _ = issue38649{X: 0}
 
 func main() {
+	pass := true
+
+	// The Go translation of bitfields should not have any of the
+	// bitfield types. The order in which bitfields are laid out
+	// in memory is implementation defined, so we can't easily
+	// know how a bitfield should correspond to a Go type, even if
+	// it appears to be aligned correctly.
+	bitfieldType := reflect.TypeOf(bitfields{})
+	check := func(name string) {
+		_, ok := bitfieldType.FieldByName(name)
+		if ok {
+			fmt.Fprintf(os.Stderr, "found unexpected bitfields field %s\n", name)
+			pass = false
+		}
+	}
+	check("Short1")
+	check("Short2")
+	check("Short3")
+
+	if !pass {
+		os.Exit(1)
+	}
 }
diff --git a/misc/cgo/testgodefs/testgodefs_test.go b/misc/cgo/testgodefs/testgodefs_test.go
index e4085f9..4c2312c 100644
--- a/misc/cgo/testgodefs/testgodefs_test.go
+++ b/misc/cgo/testgodefs/testgodefs_test.go
@@ -19,6 +19,7 @@
 // import "C" block.  Add more tests here.
 var filePrefixes = []string{
 	"anonunion",
+	"bitfields",
 	"issue8478",
 	"fieldtypedef",
 	"issue37479",
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index 9179b54..eb6c1a5 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -2831,21 +2831,11 @@
 		tgo := t.Go
 		size := t.Size
 		talign := t.Align
-		if f.BitSize > 0 {
-			switch f.BitSize {
-			case 8, 16, 32, 64:
-			default:
-				continue
-			}
-			size = f.BitSize / 8
-			name := tgo.(*ast.Ident).String()
-			if strings.HasPrefix(name, "int") {
-				name = "int"
-			} else {
-				name = "uint"
-			}
-			tgo = ast.NewIdent(name + fmt.Sprint(f.BitSize))
-			talign = size
+		if f.BitOffset > 0 || f.BitSize > 0 {
+			// The layout of bitfields is implementation defined,
+			// so we don't know how they correspond to Go fields
+			// even if they are aligned at byte boundaries.
+			continue
 		}
 
 		if talign > 0 && f.ByteOffset%talign != 0 {