cmd/compile: don't include instantiating types in type hash

This CL is a bit overkill, but it is pretty safe for 1.18. We'll
want to revisit for 1.19 so we can avoid the hash collisions between
types, e.g. G[int] and G[float64], that will cause some slowdowns
(but not incorrect behavior). Thanks Cherry for the simple idea.

Fixes #51250

Change-Id: I68130e09ba68e7cc35687bc623f63547bc552867
Reviewed-on: https://go-review.googlesource.com/c/go/+/389474
Trust: Keith Randall <khr@golang.org>
Run-TryBot: Keith Randall <khr@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Keith Randall 2022-03-02 15:08:23 -08:00
parent bcb89fc17a
commit d3672054fb
6 changed files with 85 additions and 9 deletions

View File

@ -1428,7 +1428,7 @@ func WriteBasicTypes() {
type typeAndStr struct { type typeAndStr struct {
t *types.Type t *types.Type
short string // "short" here means NameString short string // "short" here means TypeSymName
regular string regular string
} }

View File

@ -72,6 +72,7 @@ const (
fmtDebug fmtDebug
fmtTypeID fmtTypeID
fmtTypeIDName fmtTypeIDName
fmtTypeIDHash
) )
// Sym // Sym
@ -144,10 +145,21 @@ func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
if q := pkgqual(s.Pkg, verb, mode); q != "" { if q := pkgqual(s.Pkg, verb, mode); q != "" {
b.WriteString(q) b.WriteString(q)
b.WriteByte('.') b.WriteByte('.')
if mode == fmtTypeIDName { switch mode {
case fmtTypeIDName:
// If name is a generic instantiation, it might have local package placeholders // If name is a generic instantiation, it might have local package placeholders
// in it. Replace those placeholders with the package name. See issue 49547. // in it. Replace those placeholders with the package name. See issue 49547.
name = strings.Replace(name, LocalPkg.Prefix, q, -1) name = strings.Replace(name, LocalPkg.Prefix, q, -1)
case fmtTypeIDHash:
// If name is a generic instantiation, don't hash the instantiating types.
// This isn't great, but it is safe. If we hash the instantiating types, then
// we need to make sure they have just the package name. At this point, they
// either have "", or the whole package path, and it is hard to reconcile
// the two without depending on -p (which we might do someday).
// See issue 51250.
if i := strings.Index(name, "["); i >= 0 {
name = name[:i]
}
} }
} }
b.WriteString(name) b.WriteString(name)
@ -176,7 +188,7 @@ func pkgqual(pkg *Pkg, verb rune, mode fmtMode) string {
case fmtDebug: case fmtDebug:
return pkg.Name return pkg.Name
case fmtTypeIDName: case fmtTypeIDName, fmtTypeIDHash:
// dcommontype, typehash // dcommontype, typehash
return pkg.Name return pkg.Name
@ -334,7 +346,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
if t == AnyType || t == ByteType || t == RuneType { if t == AnyType || t == ByteType || t == RuneType {
// in %-T mode collapse predeclared aliases with their originals. // in %-T mode collapse predeclared aliases with their originals.
switch mode { switch mode {
case fmtTypeIDName, fmtTypeID: case fmtTypeIDName, fmtTypeIDHash, fmtTypeID:
t = Types[t.Kind()] t = Types[t.Kind()]
default: default:
sconv2(b, t.Sym(), 'S', mode) sconv2(b, t.Sym(), 'S', mode)
@ -425,7 +437,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
case TPTR: case TPTR:
b.WriteByte('*') b.WriteByte('*')
switch mode { switch mode {
case fmtTypeID, fmtTypeIDName: case fmtTypeID, fmtTypeIDName, fmtTypeIDHash:
if verb == 'S' { if verb == 'S' {
tconv2(b, t.Elem(), 'S', mode, visited) tconv2(b, t.Elem(), 'S', mode, visited)
return return
@ -487,7 +499,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
case IsExported(f.Sym.Name): case IsExported(f.Sym.Name):
sconv2(b, f.Sym, 'S', mode) sconv2(b, f.Sym, 'S', mode)
default: default:
if mode != fmtTypeIDName { if mode != fmtTypeIDName && mode != fmtTypeIDHash {
mode = fmtTypeID mode = fmtTypeID
} }
sconv2(b, f.Sym, 'v', mode) sconv2(b, f.Sym, 'v', mode)
@ -557,7 +569,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
b.WriteByte(byte(open)) b.WriteByte(byte(open))
fieldVerb := 'v' fieldVerb := 'v'
switch mode { switch mode {
case fmtTypeID, fmtTypeIDName, fmtGo: case fmtTypeID, fmtTypeIDName, fmtTypeIDHash, fmtGo:
// no argument names on function signature, and no "noescape"/"nosplit" tags // no argument names on function signature, and no "noescape"/"nosplit" tags
fieldVerb = 'S' fieldVerb = 'S'
} }
@ -691,7 +703,7 @@ func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Ty
if name == ".F" { if name == ".F" {
name = "F" // Hack for toolstash -cmp. name = "F" // Hack for toolstash -cmp.
} }
if !IsExported(name) && mode != fmtTypeIDName { if !IsExported(name) && mode != fmtTypeIDName && mode != fmtTypeIDHash {
name = sconv(s, 0, mode) // qualify non-exported names (used on structs, not on funarg) name = sconv(s, 0, mode) // qualify non-exported names (used on structs, not on funarg)
} }
} else { } else {
@ -759,7 +771,7 @@ func FmtConst(v constant.Value, sharp bool) string {
// TypeHash computes a hash value for type t to use in type switch statements. // TypeHash computes a hash value for type t to use in type switch statements.
func TypeHash(t *Type) uint32 { func TypeHash(t *Type) uint32 {
p := t.NameString() p := tconv(t, 0, fmtTypeIDHash)
// Using MD5 is overkill, but reduces accidental collisions. // Using MD5 is overkill, but reduces accidental collisions.
h := md5.Sum([]byte(p)) h := md5.Sum([]byte(p))

View File

@ -0,0 +1,9 @@
// Copyright 2022 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 a
type G[T any] struct {
x T
}

View File

@ -0,0 +1,24 @@
// Copyright 2022 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 b
import "./a"
type T struct { a int }
var I interface{} = a.G[T]{}
//go:noinline
func F(x interface{}) {
switch x.(type) {
case a.G[T]:
case int:
panic("bad")
case float64:
panic("bad")
default:
panic("bad")
}
}

View File

@ -0,0 +1,24 @@
// Copyright 2022 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
import (
"./a"
"./b"
)
func main() {
switch b.I.(type) {
case a.G[b.T]:
case int:
panic("bad")
case float64:
panic("bad")
default:
panic("bad")
}
b.F(a.G[b.T]{})
}

View File

@ -0,0 +1,7 @@
// rundir
// Copyright 2022 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 ignored