--- /dev/null
+// Copyright 2019 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.
+
+// Don't make a private copy of an array when taking the address of an
+// element.
+
+package cgotest
+
+// #include <string.h>
+import "C"
+
+import (
+ "testing"
+ "unsafe"
+)
+
+func test30065(t *testing.T) {
+ var a [256]byte
+ b := []byte("a")
+ C.memcpy(unsafe.Pointer(&a), unsafe.Pointer(&b[0]), 1)
+ if a[0] != 'a' {
+ t.Errorf("&a failed: got %c, want %c", a[0], 'a')
+ }
+
+ b = []byte("b")
+ C.memcpy(unsafe.Pointer(&a[0]), unsafe.Pointer(&b[0]), 1)
+ if a[0] != 'b' {
+ t.Errorf("&a[0] failed: got %c, want %c", a[0], 'b')
+ }
+
+ d := make([]byte, 256)
+ b = []byte("c")
+ C.memcpy(unsafe.Pointer(&d[0]), unsafe.Pointer(&b[0]), 1)
+ if d[0] != 'c' {
+ t.Errorf("&d[0] failed: got %c, want %c", d[0], 'c')
+ }
+}
}
// checkIndex checks whether arg has the form &a[i], possibly inside
-// type conversions. If so, it writes
+// type conversions. If so, then in the general case it writes
// _cgoIndexNN := a
// _cgoNN := &cgoIndexNN[i] // with type conversions, if any
// to sb, and writes
// _cgoCheckPointer(_cgoNN, _cgoIndexNN)
-// to sbCheck, and returns true. This tells _cgoCheckPointer to check
-// the complete contents of the slice or array being indexed, but no
-// other part of the memory allocation.
+// to sbCheck, and returns true. If a is a simple variable or field reference,
+// it writes
+// _cgoIndexNN := &a
+// and dereferences the uses of _cgoIndexNN. Taking the address avoids
+// making a copy of an array.
+//
+// This tells _cgoCheckPointer to check the complete contents of the
+// slice or array being indexed, but no other part of the memory allocation.
func (p *Package) checkIndex(sb, sbCheck *bytes.Buffer, arg ast.Expr, i int) bool {
// Strip type conversions.
x := arg
return false
}
- fmt.Fprintf(sb, "_cgoIndex%d := %s; ", i, gofmtPos(index.X, index.X.Pos()))
+ addr := ""
+ deref := ""
+ if p.isVariable(index.X) {
+ addr = "&"
+ deref = "*"
+ }
+
+ fmt.Fprintf(sb, "_cgoIndex%d := %s%s; ", i, addr, gofmtPos(index.X, index.X.Pos()))
origX := index.X
index.X = ast.NewIdent(fmt.Sprintf("_cgoIndex%d", i))
+ if deref == "*" {
+ index.X = &ast.StarExpr{X: index.X}
+ }
fmt.Fprintf(sb, "_cgo%d := %s; ", i, gofmtPos(arg, arg.Pos()))
index.X = origX
- fmt.Fprintf(sbCheck, "_cgoCheckPointer(_cgo%d, _cgoIndex%d); ", i, i)
+ fmt.Fprintf(sbCheck, "_cgoCheckPointer(_cgo%d, %s_cgoIndex%d); ", i, deref, i)
return true
}
return false
}
+// isVariable reports whether x is a variable, possibly with field references.
+func (p *Package) isVariable(x ast.Expr) bool {
+ switch x := x.(type) {
+ case *ast.Ident:
+ return true
+ case *ast.SelectorExpr:
+ return p.isVariable(x.X)
+ }
+ return false
+}
+
// rewriteUnsafe returns a version of t with references to unsafe.Pointer
// rewritten to use _cgo_unsafe.Pointer instead.
func (p *Package) rewriteUnsafe(t ast.Expr) ast.Expr {