1// Copyright (c) 2019 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package field 6 7import ( 8 "testing" 9 "testing/quick" 10) 11 12func checkAliasingOneArg(f func(v, x *Element) *Element) func(v, x Element) bool { 13 return func(v, x Element) bool { 14 x1, v1 := x, x 15 16 // Calculate a reference f(x) without aliasing. 17 if out := f(&v, &x); out != &v && isInBounds(out) { 18 return false 19 } 20 21 // Test aliasing the argument and the receiver. 22 if out := f(&v1, &v1); out != &v1 || v1 != v { 23 return false 24 } 25 26 // Ensure the arguments was not modified. 27 return x == x1 28 } 29} 30 31func checkAliasingTwoArgs(f func(v, x, y *Element) *Element) func(v, x, y Element) bool { 32 return func(v, x, y Element) bool { 33 x1, y1, v1 := x, y, Element{} 34 35 // Calculate a reference f(x, y) without aliasing. 36 if out := f(&v, &x, &y); out != &v && isInBounds(out) { 37 return false 38 } 39 40 // Test aliasing the first argument and the receiver. 41 v1 = x 42 if out := f(&v1, &v1, &y); out != &v1 || v1 != v { 43 return false 44 } 45 // Test aliasing the second argument and the receiver. 46 v1 = y 47 if out := f(&v1, &x, &v1); out != &v1 || v1 != v { 48 return false 49 } 50 51 // Calculate a reference f(x, x) without aliasing. 52 if out := f(&v, &x, &x); out != &v { 53 return false 54 } 55 56 // Test aliasing the first argument and the receiver. 57 v1 = x 58 if out := f(&v1, &v1, &x); out != &v1 || v1 != v { 59 return false 60 } 61 // Test aliasing the second argument and the receiver. 62 v1 = x 63 if out := f(&v1, &x, &v1); out != &v1 || v1 != v { 64 return false 65 } 66 // Test aliasing both arguments and the receiver. 67 v1 = x 68 if out := f(&v1, &v1, &v1); out != &v1 || v1 != v { 69 return false 70 } 71 72 // Ensure the arguments were not modified. 73 return x == x1 && y == y1 74 } 75} 76 77// TestAliasing checks that receivers and arguments can alias each other without 78// leading to incorrect results. That is, it ensures that it's safe to write 79// 80// v.Invert(v) 81// 82// or 83// 84// v.Add(v, v) 85// 86// without any of the inputs getting clobbered by the output being written. 87func TestAliasing(t *testing.T) { 88 type target struct { 89 name string 90 oneArgF func(v, x *Element) *Element 91 twoArgsF func(v, x, y *Element) *Element 92 } 93 for _, tt := range []target{ 94 {name: "Absolute", oneArgF: (*Element).Absolute}, 95 {name: "Invert", oneArgF: (*Element).Invert}, 96 {name: "Negate", oneArgF: (*Element).Negate}, 97 {name: "Set", oneArgF: (*Element).Set}, 98 {name: "Square", oneArgF: (*Element).Square}, 99 {name: "Multiply", twoArgsF: (*Element).Multiply}, 100 {name: "Add", twoArgsF: (*Element).Add}, 101 {name: "Subtract", twoArgsF: (*Element).Subtract}, 102 { 103 name: "Select0", 104 twoArgsF: func(v, x, y *Element) *Element { 105 return (*Element).Select(v, x, y, 0) 106 }, 107 }, 108 { 109 name: "Select1", 110 twoArgsF: func(v, x, y *Element) *Element { 111 return (*Element).Select(v, x, y, 1) 112 }, 113 }, 114 } { 115 var err error 116 switch { 117 case tt.oneArgF != nil: 118 err = quick.Check(checkAliasingOneArg(tt.oneArgF), &quick.Config{MaxCountScale: 1 << 8}) 119 case tt.twoArgsF != nil: 120 err = quick.Check(checkAliasingTwoArgs(tt.twoArgsF), &quick.Config{MaxCountScale: 1 << 8}) 121 } 122 if err != nil { 123 t.Errorf("%v: %v", tt.name, err) 124 } 125 } 126} 127