Source file test/fixedbugs/issue81266.go

     1  // run
     2  
     3  // Copyright 2026 The Go Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  // Issue 81266: check that we generate correct code when
     8  // using the subroutine mechanism in the equality/hash generator.
     9  
    10  package main
    11  
    12  import (
    13  	"fmt"
    14  	"unsafe"
    15  )
    16  
    17  func check(name string, got, want bool) {
    18  	if got != want {
    19  		panic(fmt.Sprintf("%s: got %v want %v", name, got, want))
    20  	}
    21  }
    22  
    23  // Big is large enough for subroutine treatment, and has trailing padding.
    24  type Big struct {
    25  	Filler [2000]byte
    26  	S      string
    27  	B      byte
    28  }
    29  
    30  // Outer embeds Big as a non-top-level field, and has a field after
    31  // it. There is padding between the two fields.
    32  type Outer struct {
    33  	Big Big
    34  	X   byte
    35  }
    36  
    37  // fill sets every byte of the size bytes at p to b. Used to poison
    38  // padding bytes with garbage, so that tests which pass despite
    39  // differing padding prove the padding is correctly ignored.
    40  func fill(p unsafe.Pointer, size uintptr, b byte) {
    41  	buf := unsafe.Slice((*byte)(p), size)
    42  	for i := range buf {
    43  		buf[i] = b
    44  	}
    45  }
    46  
    47  var filler [2000]byte
    48  
    49  func init() {
    50  	for i := range filler {
    51  		filler[i] = byte(i)
    52  	}
    53  }
    54  
    55  func mkBig(s string, b byte, pad byte) Big {
    56  	var x Big
    57  	fill(unsafe.Pointer(&x), unsafe.Sizeof(x), pad)
    58  	x.Filler = filler
    59  	x.S = s
    60  	x.B = b
    61  	return x
    62  }
    63  
    64  func testBasic() {
    65  	a := Outer{Big: mkBig("foo", 7, 0xAA), X: 1}
    66  	c := Outer{Big: mkBig("foo", 7, 0x55), X: 1}
    67  	check("equal despite differing padding inside Big", a == c, true)
    68  
    69  	d := Outer{Big: mkBig("foo", 7, 0xAA), X: 2}
    70  	check("differ in trailing field X", a == d, false)
    71  
    72  	e := Outer{Big: mkBig("bar", 7, 0xAA), X: 1}
    73  	check("differ in Big.S", a == e, false)
    74  
    75  	g := Outer{Big: mkBig("foo", 9, 0xAA), X: 1}
    76  	check("differ in Big.B", a == g, false)
    77  
    78  	h := a
    79  	h.Big.Filler[1000] ^= 0xFF
    80  	check("differ in Big.Filler", a == h, false)
    81  }
    82  
    83  // testPadding makes sure different paddings don't prevent equality.
    84  func testPadding() {
    85  	var a, c Outer
    86  	fill(unsafe.Pointer(&a), unsafe.Sizeof(a), 0x11)
    87  	fill(unsafe.Pointer(&c), unsafe.Sizeof(c), 0x99)
    88  	a.Big.Filler = filler
    89  	c.Big.Filler = filler
    90  	a.Big.S = "padding-check"
    91  	c.Big.S = "padding-check"
    92  	a.Big.B = 42
    93  	c.Big.B = 42
    94  	a.X = 5
    95  	c.X = 5
    96  	check("equal with garbage-filled padding", a == c, true)
    97  }
    98  
    99  // testHash exercises the hash side of the subroutine mechanism.
   100  func testHash() {
   101  	m := make(map[Outer]int)
   102  	k1 := Outer{Big: mkBig("k1", 1, 0), X: 1}
   103  	k2 := Outer{Big: mkBig("k2", 2, 0), X: 2}
   104  	m[k1] = 100
   105  	m[k2] = 200
   106  
   107  	look1 := Outer{Big: mkBig("k1", 1, 0xFF), X: 1} // differing padding
   108  	look2 := Outer{Big: mkBig("k2", 2, 0xFF), X: 2}
   109  	v1, ok1 := m[look1]
   110  	v2, ok2 := m[look2]
   111  	check("map lookup k1 found", ok1, true)
   112  	check("map lookup k2 found", ok2, true)
   113  	if v1 != 100 {
   114  		panic(fmt.Sprintf("map k1 value: got %v want 100", v1))
   115  	}
   116  	if v2 != 200 {
   117  		panic(fmt.Sprintf("map k2 value: got %v want 200", v2))
   118  	}
   119  
   120  	notFound := Outer{Big: mkBig("k1", 1, 0), X: 99}
   121  	_, ok3 := m[notFound]
   122  	check("map lookup miss", ok3, false)
   123  }
   124  
   125  // testNested exercises two levels of subroutine nesting
   126  // (Outer2 -> Level1 -> Big).
   127  func testNested() {
   128  	// Level1 embeds Big, so it becomes a subroutine when embedded
   129  	// in Outer2. This produces two levels of subroutine nesting.
   130  	type Level1 struct {
   131  		B  Big
   132  		F2 [2000]byte
   133  		T  string
   134  		C  byte
   135  	}
   136  
   137  	type Outer2 struct {
   138  		L1 Level1
   139  		Y  byte
   140  	}
   141  
   142  	mkLevel1 := func(s1, s2 string, b1, b2 byte) Level1 {
   143  		var l Level1
   144  		l.B = mkBig(s1, b1, 0)
   145  		l.F2 = filler
   146  		l.T = s2
   147  		l.C = b2
   148  		return l
   149  	}
   150  
   151  	a := Outer2{L1: mkLevel1("inner1", "outer1", 1, 2), Y: 9}
   152  	c := Outer2{L1: mkLevel1("inner1", "outer1", 1, 2), Y: 9}
   153  	check("nested equal", a == c, true)
   154  
   155  	d := a
   156  	d.L1.B.S = "changed"
   157  	check("nested differ in deepest field", a == d, false)
   158  
   159  	e := a
   160  	e.L1.T = "changed"
   161  	check("nested differ in Level1 field", a == e, false)
   162  
   163  	g := a
   164  	g.Y = 10
   165  	check("nested differ in outer field", a == g, false)
   166  
   167  	mm := make(map[Outer2]string)
   168  	mm[a] = "a-value"
   169  	if got := mm[c]; got != "a-value" {
   170  		panic(fmt.Sprintf("nested map lookup: got %q want %q", got, "a-value"))
   171  	}
   172  }
   173  
   174  // testArray exercises an array of large elements, each of which uses
   175  // the subroutine mechanism.
   176  func testArray() {
   177  	type BigArr struct {
   178  		A [3]Big
   179  	}
   180  	var a, c BigArr
   181  	for i := range a.A {
   182  		a.A[i] = mkBig(fmt.Sprintf("elem%d", i), byte(i), byte(i))
   183  		c.A[i] = mkBig(fmt.Sprintf("elem%d", i), byte(i), byte(i+100)) // different padding
   184  	}
   185  	check("array equal despite differing padding", a == c, true)
   186  
   187  	d := a
   188  	d.A[2].S = "different"
   189  	check("array differ in one element", a == d, false)
   190  }
   191  
   192  // testBoundary checks types right at the maxExpandSize (1024) cutoff,
   193  // on both sides.
   194  func testBoundary() {
   195  	type Exactly1024 struct {
   196  		F [1024 - 16]byte // + string(16) = 1024 exactly, on a 64-bit platform
   197  		S string
   198  	}
   199  	type Container1024 struct {
   200  		V Exactly1024
   201  		X byte
   202  	}
   203  	var a, c Container1024
   204  	a.V.S = "same"
   205  	c.V.S = "same"
   206  	a.X = 1
   207  	c.X = 1
   208  	check("boundary <=1024 equal", a == c, true)
   209  	d := a
   210  	d.V.S = "diff"
   211  	check("boundary <=1024 differ", a == d, false)
   212  
   213  	type Exactly1025 struct {
   214  		F [1025 - 16]byte
   215  		S string
   216  	}
   217  	type Container1025 struct {
   218  		V Exactly1025
   219  		X byte
   220  	}
   221  	var e, g Container1025
   222  	e.V.S = "same"
   223  	g.V.S = "same"
   224  	e.X = 1
   225  	g.X = 1
   226  	check("boundary >1024 equal", e == g, true)
   227  	h := e
   228  	h.V.S = "diff"
   229  	check("boundary >1024 differ", e == h, false)
   230  }
   231  
   232  func main() {
   233  	testBasic()
   234  	testPadding()
   235  	testHash()
   236  	testNested()
   237  	testArray()
   238  	testBoundary()
   239  }
   240  

View as plain text