diff --git a/misc/cgo/errors/errors_test.go b/misc/cgo/errors/errors_test.go index f727158c48f..e6bac0fff45 100644 --- a/misc/cgo/errors/errors_test.go +++ b/misc/cgo/errors/errors_test.go @@ -63,7 +63,7 @@ func expect(t *testing.T, file string, errors []*regexp.Regexp) { defer os.RemoveAll(dir) dst := filepath.Join(dir, strings.TrimSuffix(file, ".go")) - cmd := exec.Command("go", "build", "-gcflags=-L", "-o="+dst, path(file)) // TODO(gri) no need for -gcflags=-L if go tool is adjusted + cmd := exec.Command("go", "build", "-gcflags=-L -e", "-o="+dst, path(file)) // TODO(gri) no need for -gcflags=-L if go tool is adjusted out, err := cmd.CombinedOutput() if err == nil { t.Errorf("expected cgo to fail but it succeeded") @@ -107,21 +107,10 @@ func TestReportsTypeErrors(t *testing.T) { for _, file := range []string{ "err1.go", "err2.go", - "err3.go", - "issue7757.go", - "issue8442.go", "issue11097a.go", "issue11097b.go", - "issue13129.go", - "issue13423.go", - "issue13467.go", - "issue13635.go", - "issue13830.go", - "issue16116.go", - "issue16591.go", "issue18452.go", "issue18889.go", - "issue26745.go", "issue28721.go", } { check(t, file) diff --git a/misc/cgo/errors/ptr_test.go b/misc/cgo/errors/ptr_test.go index 629f4c92265..ba4f4ade648 100644 --- a/misc/cgo/errors/ptr_test.go +++ b/misc/cgo/errors/ptr_test.go @@ -7,21 +7,25 @@ package errorstest import ( - "bufio" "bytes" + "flag" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "strings" + "sync/atomic" "testing" ) +var tmp = flag.String("tmp", "", "use `dir` for temporary files and do not clean up") + // ptrTest is the tests without the boilerplate. type ptrTest struct { name string // for reporting c string // the cgo comment + c1 string // cgo comment forced into non-export cgo file imports []string // a list of imports support string // supporting functions body string // the body of the main function @@ -39,253 +43,248 @@ var ptrTests = []ptrTest{ { // Passing a pointer to a struct that contains a Go pointer. name: "ptr1", - c: `typedef struct s { int *p; } s; void f(s *ps) {}`, - body: `C.f(&C.s{new(C.int)})`, + c: `typedef struct s1 { int *p; } s1; void f1(s1 *ps) {}`, + body: `C.f1(&C.s1{new(C.int)})`, fail: true, }, { // Passing a pointer to a struct that contains a Go pointer. name: "ptr2", - c: `typedef struct s { int *p; } s; void f(s *ps) {}`, - body: `p := &C.s{new(C.int)}; C.f(p)`, + c: `typedef struct s2 { int *p; } s2; void f2(s2 *ps) {}`, + body: `p := &C.s2{new(C.int)}; C.f2(p)`, fail: true, }, { // Passing a pointer to an int field of a Go struct // that (irrelevantly) contains a Go pointer. name: "ok1", - c: `struct s { int i; int *p; }; void f(int *p) {}`, - body: `p := &C.struct_s{i: 0, p: new(C.int)}; C.f(&p.i)`, + c: `struct s3 { int i; int *p; }; void f3(int *p) {}`, + body: `p := &C.struct_s3{i: 0, p: new(C.int)}; C.f3(&p.i)`, fail: false, }, { // Passing a pointer to a pointer field of a Go struct. - name: "ptr-field", - c: `struct s { int i; int *p; }; void f(int **p) {}`, - body: `p := &C.struct_s{i: 0, p: new(C.int)}; C.f(&p.p)`, + name: "ptrfield", + c: `struct s4 { int i; int *p; }; void f4(int **p) {}`, + body: `p := &C.struct_s4{i: 0, p: new(C.int)}; C.f4(&p.p)`, fail: true, }, { // Passing a pointer to a pointer field of a Go // struct, where the field does not contain a Go // pointer, but another field (irrelevantly) does. - name: "ptr-field-ok", - c: `struct s { int *p1; int *p2; }; void f(int **p) {}`, - body: `p := &C.struct_s{p1: nil, p2: new(C.int)}; C.f(&p.p1)`, + name: "ptrfieldok", + c: `struct s5 { int *p1; int *p2; }; void f5(int **p) {}`, + body: `p := &C.struct_s5{p1: nil, p2: new(C.int)}; C.f5(&p.p1)`, fail: false, }, { // Passing the address of a slice with no Go pointers. - name: "slice-ok-1", - c: `void f(void **p) {}`, + name: "sliceok1", + c: `void f6(void **p) {}`, imports: []string{"unsafe"}, - body: `s := []unsafe.Pointer{nil}; C.f(&s[0])`, + body: `s := []unsafe.Pointer{nil}; C.f6(&s[0])`, fail: false, }, { // Passing the address of a slice with a Go pointer. - name: "slice-ptr-1", - c: `void f(void **p) {}`, + name: "sliceptr1", + c: `void f7(void **p) {}`, imports: []string{"unsafe"}, - body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f(&s[0])`, + body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f7(&s[0])`, fail: true, }, { // Passing the address of a slice with a Go pointer, // where we are passing the address of an element that // is not a Go pointer. - name: "slice-ptr-2", - c: `void f(void **p) {}`, + name: "sliceptr2", + c: `void f8(void **p) {}`, imports: []string{"unsafe"}, - body: `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f(&s[0])`, + body: `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f8(&s[0])`, fail: true, }, { // Passing the address of a slice that is an element // in a struct only looks at the slice. - name: "slice-ok-2", - c: `void f(void **p) {}`, + name: "sliceok2", + c: `void f9(void **p) {}`, imports: []string{"unsafe"}, - support: `type S struct { p *int; s []unsafe.Pointer }`, - body: `i := 0; p := &S{p:&i, s:[]unsafe.Pointer{nil}}; C.f(&p.s[0])`, + support: `type S9 struct { p *int; s []unsafe.Pointer }`, + body: `i := 0; p := &S9{p:&i, s:[]unsafe.Pointer{nil}}; C.f9(&p.s[0])`, fail: false, }, { // Passing the address of a slice of an array that is // an element in a struct, with a type conversion. - name: "slice-ok-3", - c: `void f(void* p) {}`, + name: "sliceok3", + c: `void f10(void* p) {}`, imports: []string{"unsafe"}, - support: `type S struct { p *int; a [4]byte }`, - body: `i := 0; p := &S{p:&i}; s := p.a[:]; C.f(unsafe.Pointer(&s[0]))`, + support: `type S10 struct { p *int; a [4]byte }`, + body: `i := 0; p := &S10{p:&i}; s := p.a[:]; C.f10(unsafe.Pointer(&s[0]))`, fail: false, }, { // Passing the address of a slice of an array that is // an element in a struct, with a type conversion. - name: "slice-ok-4", - c: `typedef void* PV; void f(PV p) {}`, + name: "sliceok4", + c: `typedef void* PV11; void f11(PV11 p) {}`, imports: []string{"unsafe"}, - support: `type S struct { p *int; a [4]byte }`, - body: `i := 0; p := &S{p:&i}; C.f(C.PV(unsafe.Pointer(&p.a[0])))`, + support: `type S11 struct { p *int; a [4]byte }`, + body: `i := 0; p := &S11{p:&i}; C.f11(C.PV11(unsafe.Pointer(&p.a[0])))`, fail: false, }, { // Passing the address of a static variable with no // pointers doesn't matter. name: "varok", - c: `void f(char** parg) {}`, - support: `var hello = [...]C.char{'h', 'e', 'l', 'l', 'o'}`, - body: `parg := [1]*C.char{&hello[0]}; C.f(&parg[0])`, + c: `void f12(char** parg) {}`, + support: `var hello12 = [...]C.char{'h', 'e', 'l', 'l', 'o'}`, + body: `parg := [1]*C.char{&hello12[0]}; C.f12(&parg[0])`, fail: false, }, { // Passing the address of a static variable with // pointers does matter. - name: "var", - c: `void f(char*** parg) {}`, - support: `var hello = [...]*C.char{new(C.char)}`, - body: `parg := [1]**C.char{&hello[0]}; C.f(&parg[0])`, + name: "var1", + c: `void f13(char*** parg) {}`, + support: `var hello13 = [...]*C.char{new(C.char)}`, + body: `parg := [1]**C.char{&hello13[0]}; C.f13(&parg[0])`, fail: true, }, { // Storing a Go pointer into C memory should fail. name: "barrier", c: `#include - char **f1() { return malloc(sizeof(char*)); } - void f2(char **p) {}`, - body: `p := C.f1(); *p = new(C.char); C.f2(p)`, + char **f14a() { return malloc(sizeof(char*)); } + void f14b(char **p) {}`, + body: `p := C.f14a(); *p = new(C.char); C.f14b(p)`, fail: true, expensive: true, }, { // Storing a Go pointer into C memory by assigning a // large value should fail. - name: "barrier-struct", + name: "barrierstruct", c: `#include - struct s { char *a[10]; }; - struct s *f1() { return malloc(sizeof(struct s)); } - void f2(struct s *p) {}`, - body: `p := C.f1(); p.a = [10]*C.char{new(C.char)}; C.f2(p)`, + struct s15 { char *a[10]; }; + struct s15 *f15() { return malloc(sizeof(struct s15)); } + void f15b(struct s15 *p) {}`, + body: `p := C.f15(); p.a = [10]*C.char{new(C.char)}; C.f15b(p)`, fail: true, expensive: true, }, { // Storing a Go pointer into C memory using a slice // copy should fail. - name: "barrier-slice", + name: "barrierslice", c: `#include - struct s { char *a[10]; }; - struct s *f1() { return malloc(sizeof(struct s)); } - void f2(struct s *p) {}`, - body: `p := C.f1(); copy(p.a[:], []*C.char{new(C.char)}); C.f2(p)`, + struct s16 { char *a[10]; }; + struct s16 *f16() { return malloc(sizeof(struct s16)); } + void f16b(struct s16 *p) {}`, + body: `p := C.f16(); copy(p.a[:], []*C.char{new(C.char)}); C.f16b(p)`, fail: true, expensive: true, }, { // A very large value uses a GC program, which is a // different code path. - name: "barrier-gcprog-array", + name: "barriergcprogarray", c: `#include - struct s { char *a[32769]; }; - struct s *f1() { return malloc(sizeof(struct s)); } - void f2(struct s *p) {}`, - body: `p := C.f1(); p.a = [32769]*C.char{new(C.char)}; C.f2(p)`, + struct s17 { char *a[32769]; }; + struct s17 *f17() { return malloc(sizeof(struct s17)); } + void f17b(struct s17 *p) {}`, + body: `p := C.f17(); p.a = [32769]*C.char{new(C.char)}; C.f17b(p)`, fail: true, expensive: true, }, { // Similar case, with a source on the heap. - name: "barrier-gcprog-array-heap", + name: "barriergcprogarrayheap", c: `#include - struct s { char *a[32769]; }; - struct s *f1() { return malloc(sizeof(struct s)); } - void f2(struct s *p) {} - void f3(void *p) {}`, + struct s18 { char *a[32769]; }; + struct s18 *f18() { return malloc(sizeof(struct s18)); } + void f18b(struct s18 *p) {} + void f18c(void *p) {}`, imports: []string{"unsafe"}, - body: `p := C.f1(); n := &[32769]*C.char{new(C.char)}; p.a = *n; C.f2(p); n[0] = nil; C.f3(unsafe.Pointer(n))`, + body: `p := C.f18(); n := &[32769]*C.char{new(C.char)}; p.a = *n; C.f18b(p); n[0] = nil; C.f18c(unsafe.Pointer(n))`, fail: true, expensive: true, }, { // A GC program with a struct. - name: "barrier-gcprog-struct", + name: "barriergcprogstruct", c: `#include - struct s { char *a[32769]; }; - struct s2 { struct s f; }; - struct s2 *f1() { return malloc(sizeof(struct s2)); } - void f2(struct s2 *p) {}`, - body: `p := C.f1(); p.f = C.struct_s{[32769]*C.char{new(C.char)}}; C.f2(p)`, + struct s19a { char *a[32769]; }; + struct s19b { struct s19a f; }; + struct s19b *f19() { return malloc(sizeof(struct s19b)); } + void f19b(struct s19b *p) {}`, + body: `p := C.f19(); p.f = C.struct_s19a{[32769]*C.char{new(C.char)}}; C.f19b(p)`, fail: true, expensive: true, }, { // Similar case, with a source on the heap. - name: "barrier-gcprog-struct-heap", + name: "barriergcprogstructheap", c: `#include - struct s { char *a[32769]; }; - struct s2 { struct s f; }; - struct s2 *f1() { return malloc(sizeof(struct s2)); } - void f2(struct s2 *p) {} - void f3(void *p) {}`, + struct s20a { char *a[32769]; }; + struct s20b { struct s20a f; }; + struct s20b *f20() { return malloc(sizeof(struct s20b)); } + void f20b(struct s20b *p) {} + void f20c(void *p) {}`, imports: []string{"unsafe"}, - body: `p := C.f1(); n := &C.struct_s{[32769]*C.char{new(C.char)}}; p.f = *n; C.f2(p); n.a[0] = nil; C.f3(unsafe.Pointer(n))`, + body: `p := C.f20(); n := &C.struct_s20a{[32769]*C.char{new(C.char)}}; p.f = *n; C.f20b(p); n.a[0] = nil; C.f20c(unsafe.Pointer(n))`, fail: true, expensive: true, }, { // Exported functions may not return Go pointers. name: "export1", - c: `extern unsigned char *GoFn();`, - support: `//export GoFn - func GoFn() *byte { return new(byte) }`, - body: `C.GoFn()`, + c: `extern unsigned char *GoFn21();`, + support: `//export GoFn21 + func GoFn21() *byte { return new(byte) }`, + body: `C.GoFn21()`, fail: true, }, { // Returning a C pointer is fine. name: "exportok", c: `#include - extern unsigned char *GoFn();`, - support: `//export GoFn - func GoFn() *byte { return (*byte)(C.malloc(1)) }`, - body: `C.GoFn()`, + extern unsigned char *GoFn22();`, + support: `//export GoFn22 + func GoFn22() *byte { return (*byte)(C.malloc(1)) }`, + body: `C.GoFn22()`, }, { // Passing a Go string is fine. - name: "pass-string", + name: "passstring", c: `#include - typedef struct { const char *p; ptrdiff_t n; } gostring; - gostring f(gostring s) { return s; }`, + typedef struct { const char *p; ptrdiff_t n; } gostring23; + gostring23 f23(gostring23 s) { return s; }`, imports: []string{"unsafe"}, - body: `s := "a"; r := C.f(*(*C.gostring)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`, + body: `s := "a"; r := C.f23(*(*C.gostring23)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`, }, { // Passing a slice of Go strings fails. - name: "pass-string-slice", - c: `void f(void *p) {}`, + name: "passstringslice", + c: `void f24(void *p) {}`, imports: []string{"strings", "unsafe"}, - support: `type S struct { a [1]string }`, - body: `s := S{a:[1]string{strings.Repeat("a", 2)}}; C.f(unsafe.Pointer(&s.a[0]))`, + support: `type S24 struct { a [1]string }`, + body: `s := S24{a:[1]string{strings.Repeat("a", 2)}}; C.f24(unsafe.Pointer(&s.a[0]))`, fail: true, }, { // Exported functions may not return strings. - name: "ret-string", - c: `extern void f();`, + name: "retstring", + c: `extern void f25();`, imports: []string{"strings"}, - support: `//export GoStr - func GoStr() string { return strings.Repeat("a", 2) }`, - body: `C.f()`, - extra: []extra{ - { - "call.c", - `#include - typedef struct { const char *p; ptrdiff_t n; } gostring; - extern gostring GoStr(); - void f() { GoStr(); }`, - }, - }, + support: `//export GoStr25 + func GoStr25() string { return strings.Repeat("a", 2) }`, + body: `C.f25()`, + c1: `#include + typedef struct { const char *p; ptrdiff_t n; } gostring25; + extern gostring25 GoStr25(); + void f25() { GoStr25(); }`, fail: true, }, { @@ -296,37 +295,37 @@ var ptrTests = []ptrTest{ // that is, we are testing something that is not unsafe. name: "ptrdata1", c: `#include - void f(void* p) {}`, + void f26(void* p) {}`, imports: []string{"unsafe"}, - support: `type S struct { p *int; a [8*8]byte; u uintptr }`, - body: `i := 0; p := &S{u:uintptr(unsafe.Pointer(&i))}; q := (*S)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f(unsafe.Pointer(q))`, + support: `type S26 struct { p *int; a [8*8]byte; u uintptr }`, + body: `i := 0; p := &S26{u:uintptr(unsafe.Pointer(&i))}; q := (*S26)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f26(unsafe.Pointer(q))`, fail: false, }, { // Like ptrdata1, but with a type that uses a GC program. name: "ptrdata2", c: `#include - void f(void* p) {}`, + void f27(void* p) {}`, imports: []string{"unsafe"}, - support: `type S struct { p *int; a [32769*8]byte; q *int; u uintptr }`, - body: `i := 0; p := S{u:uintptr(unsafe.Pointer(&i))}; q := (*S)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f(unsafe.Pointer(q))`, + support: `type S27 struct { p *int; a [32769*8]byte; q *int; u uintptr }`, + body: `i := 0; p := S27{u:uintptr(unsafe.Pointer(&i))}; q := (*S27)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f27(unsafe.Pointer(q))`, fail: false, }, { // Check deferred pointers when they are used, not // when the defer statement is run. - name: "defer", - c: `typedef struct s { int *p; } s; void f(s *ps) {}`, - body: `p := &C.s{}; defer C.f(p); p.p = new(C.int)`, + name: "defer1", + c: `typedef struct s28 { int *p; } s28; void f28(s28 *ps) {}`, + body: `p := &C.s28{}; defer C.f28(p); p.p = new(C.int)`, fail: true, }, { // Check a pointer to a union if the union has any // pointer fields. name: "union1", - c: `typedef union { char **p; unsigned long i; } u; void f(u *pu) {}`, + c: `typedef union { char **p; unsigned long i; } u29; void f29(u29 *pu) {}`, imports: []string{"unsafe"}, - body: `var b C.char; p := &b; C.f((*C.u)(unsafe.Pointer(&p)))`, + body: `var b C.char; p := &b; C.f29((*C.u29)(unsafe.Pointer(&p)))`, fail: true, }, { @@ -336,55 +335,55 @@ var ptrTests = []ptrTest{ // integer that happens to have the same // representation as a pointer. name: "union2", - c: `typedef union { unsigned long i; } u; void f(u *pu) {}`, + c: `typedef union { unsigned long i; } u39; void f39(u39 *pu) {}`, imports: []string{"unsafe"}, - body: `var b C.char; p := &b; C.f((*C.u)(unsafe.Pointer(&p)))`, + body: `var b C.char; p := &b; C.f39((*C.u39)(unsafe.Pointer(&p)))`, fail: false, }, { // Test preemption while entering a cgo call. Issue #21306. - name: "preempt-during-call", - c: `void f() {}`, + name: "preemptduringcall", + c: `void f30() {}`, imports: []string{"runtime", "sync"}, - body: `var wg sync.WaitGroup; wg.Add(100); for i := 0; i < 100; i++ { go func(i int) { for j := 0; j < 100; j++ { C.f(); runtime.GOMAXPROCS(i) }; wg.Done() }(i) }; wg.Wait()`, + body: `var wg sync.WaitGroup; wg.Add(100); for i := 0; i < 100; i++ { go func(i int) { for j := 0; j < 100; j++ { C.f30(); runtime.GOMAXPROCS(i) }; wg.Done() }(i) }; wg.Wait()`, fail: false, }, { // Test poller deadline with cgocheck=2. Issue #23435. name: "deadline", - c: `#define US 10`, + c: `#define US31 10`, imports: []string{"os", "time"}, - body: `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US * time.Microsecond))`, + body: `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US31 * time.Microsecond))`, fail: false, }, { // Test for double evaluation of channel receive. - name: "chan-recv", - c: `void f(char** p) {}`, + name: "chanrecv", + c: `void f32(char** p) {}`, imports: []string{"time"}, - body: `c := make(chan []*C.char, 2); c <- make([]*C.char, 1); go func() { time.Sleep(10 * time.Second); panic("received twice from chan") }(); C.f(&(<-c)[0]);`, + body: `c := make(chan []*C.char, 2); c <- make([]*C.char, 1); go func() { time.Sleep(10 * time.Second); panic("received twice from chan") }(); C.f32(&(<-c)[0]);`, fail: false, }, { // Test that converting the address of a struct field // to unsafe.Pointer still just checks that field. // Issue #25941. - name: "struct-field", - c: `void f(void* p) {}`, + name: "structfield", + c: `void f33(void* p) {}`, imports: []string{"unsafe"}, - support: `type S struct { p *int; a [8]byte; u uintptr }`, - body: `s := &S{p: new(int)}; C.f(unsafe.Pointer(&s.a))`, + support: `type S33 struct { p *int; a [8]byte; u uintptr }`, + body: `s := &S33{p: new(int)}; C.f33(unsafe.Pointer(&s.a))`, fail: false, }, { // Test that converting multiple struct field // addresses to unsafe.Pointer still just checks those // fields. Issue #25941. - name: "struct-field-2", - c: `void f(void* p, int r, void* s) {}`, + name: "structfield2", + c: `void f34(void* p, int r, void* s) {}`, imports: []string{"unsafe"}, - support: `type S struct { a [8]byte; p *int; b int64; }`, - body: `s := &S{p: new(int)}; C.f(unsafe.Pointer(&s.a), 32, unsafe.Pointer(&s.b))`, + support: `type S34 struct { a [8]byte; p *int; b int64; }`, + body: `s := &S34{p: new(int)}; C.f34(unsafe.Pointer(&s.a), 32, unsafe.Pointer(&s.b))`, fail: false, }, { @@ -392,18 +391,18 @@ var ptrTests = []ptrTest{ // evaluated when a deferred function is deferred, not // when it is run. name: "defer2", - c: `void f(char **pc) {}`, - support: `type S1 struct { s []*C.char }; type S2 struct { ps *S1 }`, - body: `p := &S2{&S1{[]*C.char{nil}}}; defer C.f(&p.ps.s[0]); p.ps = nil`, + c: `void f35(char **pc) {}`, + support: `type S35a struct { s []*C.char }; type S35b struct { ps *S35a }`, + body: `p := &S35b{&S35a{[]*C.char{nil}}}; defer C.f35(&p.ps.s[0]); p.ps = nil`, fail: false, }, { // Test that indexing into a function call still // examines only the slice being indexed. name: "buffer", - c: `void f(void *p) {}`, + c: `void f36(void *p) {}`, imports: []string{"bytes", "unsafe"}, - body: `var b bytes.Buffer; b.WriteString("a"); C.f(unsafe.Pointer(&b.Bytes()[0]))`, + body: `var b bytes.Buffer; b.WriteString("a"); C.f36(unsafe.Pointer(&b.Bytes()[0]))`, fail: false, }, { @@ -411,8 +410,8 @@ var ptrTests = []ptrTest{ name: "finalizer", c: `// Nothing to declare.`, imports: []string{"os"}, - support: `func open() { os.Open(os.Args[0]) }; var G [][]byte`, - body: `for i := 0; i < 10000; i++ { G = append(G, make([]byte, 4096)); if i % 100 == 0 { G = nil; open() } }`, + support: `func open37() { os.Open(os.Args[0]) }; var G37 [][]byte`, + body: `for i := 0; i < 10000; i++ { G37 = append(G37, make([]byte, 4096)); if i % 100 == 0 { G37 = nil; open37() } }`, fail: false, }, { @@ -420,103 +419,155 @@ var ptrTests = []ptrTest{ name: "structof", c: `// Nothing to declare.`, imports: []string{"reflect"}, - support: `type MyInt int; func (i MyInt) Get() int { return int(i) }; type Getter interface { Get() int }`, - body: `t := reflect.StructOf([]reflect.StructField{{Name: "MyInt", Type: reflect.TypeOf(MyInt(0)), Anonymous: true}}); v := reflect.New(t).Elem(); v.Interface().(Getter).Get()`, + support: `type MyInt38 int; func (i MyInt38) Get() int { return int(i) }; type Getter38 interface { Get() int }`, + body: `t := reflect.StructOf([]reflect.StructField{{Name: "MyInt38", Type: reflect.TypeOf(MyInt38(0)), Anonymous: true}}); v := reflect.New(t).Elem(); v.Interface().(Getter38).Get()`, fail: false, }, } func TestPointerChecks(t *testing.T) { + dir, exe := buildPtrTests(t) + + // We (TestPointerChecks) return before the parallel subtest functions do, + // so we can't just defer os.RemoveAll(dir). Instead we have to wait for + // the parallel subtests to finish. This code looks racy but is not: + // the add +1 run in serial before testOne blocks. The -1 run in parallel + // after testOne finishes. + var pending int32 for _, pt := range ptrTests { pt := pt t.Run(pt.name, func(t *testing.T) { - testOne(t, pt) + atomic.AddInt32(&pending, +1) + defer func() { + if atomic.AddInt32(&pending, -1) == 0 { + println("removing", dir) + os.RemoveAll(dir) + } + }() + testOne(t, pt, exe) }) } } -func testOne(t *testing.T, pt ptrTest) { - t.Parallel() - - gopath, err := ioutil.TempDir("", filepath.Base(t.Name())) - if err != nil { - t.Fatal(err) +func buildPtrTests(t *testing.T) (dir, exe string) { + var gopath string + if *tmp != "" { + gopath = *tmp + dir = "" + } else { + d, err := ioutil.TempDir("", filepath.Base(t.Name())) + if err != nil { + t.Fatal(err) + } + dir = d + gopath = d } - defer os.RemoveAll(gopath) src := filepath.Join(gopath, "src", "ptrtest") if err := os.MkdirAll(src, 0777); err != nil { t.Fatal(err) } - name := filepath.Join(src, fmt.Sprintf("%s.go", filepath.Base(t.Name()))) - f, err := os.Create(name) - if err != nil { - t.Fatal(err) - } + // Prepare two cgo inputs: one for standard cgo and one for //export cgo. + // (The latter cannot have C definitions, only declarations.) + var cgo1, cgo2 bytes.Buffer + fmt.Fprintf(&cgo1, "package main\n\n/*\n") + fmt.Fprintf(&cgo2, "package main\n\n/*\n") - b := bufio.NewWriter(f) - fmt.Fprintln(b, `package main`) - fmt.Fprintln(b) - fmt.Fprintln(b, `/*`) - fmt.Fprintln(b, pt.c) - fmt.Fprintln(b, `*/`) - fmt.Fprintln(b, `import "C"`) - fmt.Fprintln(b) - for _, imp := range pt.imports { - fmt.Fprintln(b, `import "`+imp+`"`) + // C code + for _, pt := range ptrTests { + cgo := &cgo1 + if strings.Contains(pt.support, "//export") { + cgo = &cgo2 + } + fmt.Fprintf(cgo, "%s\n", pt.c) + fmt.Fprintf(&cgo1, "%s\n", pt.c1) } - if len(pt.imports) > 0 { - fmt.Fprintln(b) - } - if len(pt.support) > 0 { - fmt.Fprintln(b, pt.support) - fmt.Fprintln(b) - } - fmt.Fprintln(b, `func main() {`) - fmt.Fprintln(b, pt.body) - fmt.Fprintln(b, `}`) + fmt.Fprintf(&cgo1, "*/\nimport \"C\"\n\n") + fmt.Fprintf(&cgo2, "*/\nimport \"C\"\n\n") - if err := b.Flush(); err != nil { - t.Fatalf("flushing %s: %v", name, err) - } - if err := f.Close(); err != nil { - t.Fatalf("closing %s: %v", name, err) - } + // Imports + did1 := make(map[string]bool) + did2 := make(map[string]bool) + did1["os"] = true // for ptrTestMain + fmt.Fprintf(&cgo1, "import \"os\"\n") - for _, e := range pt.extra { - if err := ioutil.WriteFile(filepath.Join(src, e.name), []byte(e.contents), 0644); err != nil { - t.Fatalf("writing %s: %v", e.name, err) + for _, pt := range ptrTests { + did := did1 + cgo := &cgo1 + if strings.Contains(pt.support, "//export") { + did = did2 + cgo = &cgo2 + } + for _, imp := range pt.imports { + if !did[imp] { + did[imp] = true + fmt.Fprintf(cgo, "import %q\n", imp) + } } } - gomod := fmt.Sprintf("module %s\n", filepath.Base(src)) - if err := ioutil.WriteFile(filepath.Join(src, "go.mod"), []byte(gomod), 0666); err != nil { - t.Fatalf("writing go.mod: %v", err) + // Func support and bodies. + for _, pt := range ptrTests { + cgo := &cgo1 + if strings.Contains(pt.support, "//export") { + cgo = &cgo2 + } + fmt.Fprintf(cgo, "%s\nfunc %s() {\n%s\n}\n", pt.support, pt.name, pt.body) } - args := func(cmd *exec.Cmd) string { - return strings.Join(cmd.Args, " ") + // Func list and main dispatch. + fmt.Fprintf(&cgo1, "var funcs = map[string]func() {\n") + for _, pt := range ptrTests { + fmt.Fprintf(&cgo1, "\t%q: %s,\n", pt.name, pt.name) + } + fmt.Fprintf(&cgo1, "}\n\n") + fmt.Fprintf(&cgo1, "%s\n", ptrTestMain) + + if err := ioutil.WriteFile(filepath.Join(src, "cgo1.go"), cgo1.Bytes(), 0666); err != nil { + t.Fatal(err) + } + if err := ioutil.WriteFile(filepath.Join(src, "cgo2.go"), cgo2.Bytes(), 0666); err != nil { + t.Fatal(err) } - cmd := exec.Command("go", "build") + cmd := exec.Command("go", "build", "-o", "ptrtest.exe") cmd.Dir = src cmd.Env = append(os.Environ(), "GOPATH="+gopath) - buf, err := cmd.CombinedOutput() + out, err := cmd.CombinedOutput() if err != nil { - t.Logf("%#q:\n%s", args(cmd), buf) - t.Fatalf("failed to build: %v", err) + t.Fatalf("go build: %v\n%s", err, out) } - exe := filepath.Join(src, filepath.Base(src)) - cmd = exec.Command(exe) - cmd.Dir = src + return dir, filepath.Join(src, "ptrtest.exe") +} + +const ptrTestMain = ` +func main() { + for _, arg := range os.Args[1:] { + f := funcs[arg] + if f == nil { + panic("missing func "+arg) + } + f() + } +} +` + +func testOne(t *testing.T, pt ptrTest, exe string) { + t.Parallel() + + newcmd := func(cgocheck string) *exec.Cmd { + cmd := exec.Command(exe, pt.name) + cmd.Env = append(os.Environ(), "GODEBUG=cgocheck="+cgocheck) + return cmd + } if pt.expensive { - cmd.Env = cgocheckEnv("1") + cmd := newcmd("1") buf, err := cmd.CombinedOutput() if err != nil { - t.Logf("%#q:\n%s", args(cmd), buf) + t.Logf("%s", buf) if pt.fail { t.Fatalf("test marked expensive, but failed when not expensive: %v", err) } else { @@ -524,54 +575,45 @@ func testOne(t *testing.T, pt ptrTest) { } } - cmd = exec.Command(exe) - cmd.Dir = src } + cmd := newcmd("") if pt.expensive { - cmd.Env = cgocheckEnv("2") + cmd = newcmd("2") } - buf, err = cmd.CombinedOutput() + buf, err := cmd.CombinedOutput() if pt.fail { if err == nil { - t.Logf("%#q:\n%s", args(cmd), buf) + t.Logf("%s", buf) t.Fatalf("did not fail as expected") } else if !bytes.Contains(buf, []byte("Go pointer")) { - t.Logf("%#q:\n%s", args(cmd), buf) + t.Logf("%s", buf) t.Fatalf("did not print expected error (failed with %v)", err) } } else { if err != nil { - t.Logf("%#q:\n%s", args(cmd), buf) + t.Logf("%s", buf) t.Fatalf("failed unexpectedly: %v", err) } if !pt.expensive { // Make sure it passes with the expensive checks. - cmd := exec.Command(exe) - cmd.Dir = src - cmd.Env = cgocheckEnv("2") + cmd := newcmd("2") buf, err := cmd.CombinedOutput() if err != nil { - t.Logf("%#q:\n%s", args(cmd), buf) + t.Logf("%s", buf) t.Fatalf("failed unexpectedly with expensive checks: %v", err) } } } if pt.fail { - cmd = exec.Command(exe) - cmd.Dir = src - cmd.Env = cgocheckEnv("0") + cmd := newcmd("0") buf, err := cmd.CombinedOutput() if err != nil { - t.Logf("%#q:\n%s", args(cmd), buf) + t.Logf("%s", buf) t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err) } } } - -func cgocheckEnv(val string) []string { - return append(os.Environ(), "GODEBUG=cgocheck="+val) -} diff --git a/misc/cgo/errors/testdata/err1.go b/misc/cgo/errors/testdata/err1.go index 2c232cf58a7..ced7443599b 100644 --- a/misc/cgo/errors/testdata/err1.go +++ b/misc/cgo/errors/testdata/err1.go @@ -10,6 +10,10 @@ package main void test() { xxx; // ERROR HERE } + +// Issue 8442. Cgo output unhelpful error messages for +// invalid C preambles. +void issue8442foo(UNDEF*); // ERROR HERE */ import "C" diff --git a/misc/cgo/errors/testdata/err2.go b/misc/cgo/errors/testdata/err2.go index 3ab410bbaac..1d22401aee5 100644 --- a/misc/cgo/errors/testdata/err2.go +++ b/misc/cgo/errors/testdata/err2.go @@ -4,10 +4,99 @@ package main +/* +#include + +typedef struct foo foo_t; +typedef struct bar bar_t; + +foo_t *foop; + +long double x = 0; + +static int transform(int x) { return x; } + +typedef void v; +void F(v** p) {} + +void fvi(void *p, int x) {} + +void fppi(int** p) {} + +int i; +void fi(int i) {} +*/ import "C" +import ( + "unsafe" +) func main() { s := "" _ = s C.malloc(s) // ERROR HERE + + x := (*C.bar_t)(nil) + C.foop = x // ERROR HERE + + // issue 13129: used to output error about C.unsignedshort with CC=clang + var x C.ushort + x = int(0) // ERROR HERE: C\.ushort + + // issue 13423 + _ = C.fopen() // ERROR HERE + + // issue 13467 + var x rune = '✈' + var _ rune = C.transform(x) // ERROR HERE: C\.int + + // issue 13635: used to output error about C.unsignedchar. + // This test tests all such types. + var ( + _ C.uchar = "uc" // ERROR HERE: C\.uchar + _ C.schar = "sc" // ERROR HERE: C\.schar + _ C.ushort = "us" // ERROR HERE: C\.ushort + _ C.uint = "ui" // ERROR HERE: C\.uint + _ C.ulong = "ul" // ERROR HERE: C\.ulong + _ C.longlong = "ll" // ERROR HERE: C\.longlong + _ C.ulonglong = "ull" // ERROR HERE: C\.ulonglong + _ C.complexfloat = "cf" // ERROR HERE: C\.complexfloat + _ C.complexdouble = "cd" // ERROR HERE: C\.complexdouble + ) + + // issue 13830 + // cgo converts C void* to Go unsafe.Pointer, so despite appearances C + // void** is Go *unsafe.Pointer. This test verifies that we detect the + // problem at build time. + { + type v [0]byte + + f := func(p **v) { + C.F((**C.v)(unsafe.Pointer(p))) // ERROR HERE + } + var p *v + f(&p) + } + + // issue 16116 + _ = C.fvi(1) // ERROR HERE + + // Issue 16591: Test that we detect an invalid call that was being + // hidden by a type conversion inserted by cgo checking. + { + type x *C.int + var p *x + C.fppi(p) // ERROR HERE + } + + // issue 26745 + _ = func(i int) int { + return C.i + 1 // ERROR HERE: :13 + } + _ = func(i int) { + C.fi(i) // ERROR HERE: :6 + } + + C.fi = C.fi // ERROR HERE + } diff --git a/misc/cgo/errors/testdata/err3.go b/misc/cgo/errors/testdata/err3.go deleted file mode 100644 index 609e1a0b748..00000000000 --- a/misc/cgo/errors/testdata/err3.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2014 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. - -package main - -/* -typedef struct foo foo_t; -typedef struct bar bar_t; - -foo_t *foop; -*/ -import "C" - -func main() { - x := (*C.bar_t)(nil) - C.foop = x // ERROR HERE -} diff --git a/misc/cgo/errors/testdata/issue13129.go b/misc/cgo/errors/testdata/issue13129.go deleted file mode 100644 index 057bce4b829..00000000000 --- a/misc/cgo/errors/testdata/issue13129.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2015 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. - -// issue 13129: used to output error about C.unsignedshort with CC=clang - -package main - -import "C" - -func main() { - var x C.ushort - x = int(0) // ERROR HERE: C\.ushort -} diff --git a/misc/cgo/errors/testdata/issue13423.go b/misc/cgo/errors/testdata/issue13423.go deleted file mode 100644 index fc191572376..00000000000 --- a/misc/cgo/errors/testdata/issue13423.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2015 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. - -package main - -// #include -import "C" - -func main() { - _ = C.fopen() // ERROR HERE -} diff --git a/misc/cgo/errors/testdata/issue13467.go b/misc/cgo/errors/testdata/issue13467.go deleted file mode 100644 index e061880ddab..00000000000 --- a/misc/cgo/errors/testdata/issue13467.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2017 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. - -package p - -/* -static int transform(int x) { return x; } -*/ -import "C" - -func F() { - var x rune = '✈' - var _ rune = C.transform(x) // ERROR HERE: C\.int -} diff --git a/misc/cgo/errors/testdata/issue13635.go b/misc/cgo/errors/testdata/issue13635.go deleted file mode 100644 index 3f38f5df4b5..00000000000 --- a/misc/cgo/errors/testdata/issue13635.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2015 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. - -// issue 13635: used to output error about C.unsignedchar. -// This test tests all such types. - -package pkg - -import "C" - -func main() { - var ( - _ C.uchar = "uc" // ERROR HERE: C\.uchar - _ C.schar = "sc" // ERROR HERE: C\.schar - _ C.ushort = "us" // ERROR HERE: C\.ushort - _ C.uint = "ui" // ERROR HERE: C\.uint - _ C.ulong = "ul" // ERROR HERE: C\.ulong - _ C.longlong = "ll" // ERROR HERE: C\.longlong - _ C.ulonglong = "ull" // ERROR HERE: C\.ulonglong - _ C.complexfloat = "cf" // ERROR HERE: C\.complexfloat - _ C.complexdouble = "cd" // ERROR HERE: C\.complexdouble - ) -} diff --git a/misc/cgo/errors/testdata/issue13830.go b/misc/cgo/errors/testdata/issue13830.go deleted file mode 100644 index ac20c82b81b..00000000000 --- a/misc/cgo/errors/testdata/issue13830.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2016 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. - -// cgo converts C void* to Go unsafe.Pointer, so despite appearances C -// void** is Go *unsafe.Pointer. This test verifies that we detect the -// problem at build time. - -package main - -// typedef void v; -// void F(v** p) {} -import "C" - -import "unsafe" - -type v [0]byte - -func f(p **v) { - C.F((**C.v)(unsafe.Pointer(p))) // ERROR HERE -} - -func main() { - var p *v - f(&p) -} diff --git a/misc/cgo/errors/testdata/issue16116.go b/misc/cgo/errors/testdata/issue16116.go deleted file mode 100644 index 1e01cab844e..00000000000 --- a/misc/cgo/errors/testdata/issue16116.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2016 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. - -package main - -// void f(void *p, int x) {} -import "C" - -func main() { - _ = C.f(1) // ERROR HERE -} diff --git a/misc/cgo/errors/testdata/issue16591.go b/misc/cgo/errors/testdata/issue16591.go deleted file mode 100644 index 10eb8403cf8..00000000000 --- a/misc/cgo/errors/testdata/issue16591.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2016 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. - -// Issue 16591: Test that we detect an invalid call that was being -// hidden by a type conversion inserted by cgo checking. - -package p - -// void f(int** p) { } -import "C" - -type x *C.int - -func F(p *x) { - C.f(p) // ERROR HERE -} diff --git a/misc/cgo/errors/testdata/issue26745.go b/misc/cgo/errors/testdata/issue26745.go deleted file mode 100644 index 0e224538db6..00000000000 --- a/misc/cgo/errors/testdata/issue26745.go +++ /dev/null @@ -1,17 +0,0 @@ -// 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. - -package main - -// int a; -// void CF(int i) {} -import "C" - -func F1(i int) int { - return C.a + 1 // ERROR HERE: :13 -} - -func F2(i int) { - C.CF(i) // ERROR HERE: :6 -} diff --git a/misc/cgo/errors/testdata/issue7757.go b/misc/cgo/errors/testdata/issue7757.go deleted file mode 100644 index 0426e9fb7ef..00000000000 --- a/misc/cgo/errors/testdata/issue7757.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2014 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. - -package main - -/* -void foo() {} -*/ -import "C" - -func main() { - C.foo = C.foo // ERROR HERE -} diff --git a/misc/cgo/errors/testdata/issue8442.go b/misc/cgo/errors/testdata/issue8442.go deleted file mode 100644 index 60477ad345e..00000000000 --- a/misc/cgo/errors/testdata/issue8442.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2014 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. - -package main - -// Issue 8442. Cgo output unhelpful error messages for -// invalid C preambles. - -/* -void issue8442foo(UNDEF*); // ERROR HERE -*/ -import "C" - -func main() { - C.issue8442foo(nil) -}