diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index 0402c2d82c..ec217be4c3 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -1428,7 +1428,7 @@ func WriteBasicTypes() { type typeAndStr struct { t *types.Type - short string // "short" here means NameString + short string // "short" here means TypeSymName regular string } diff --git a/src/cmd/compile/internal/types/fmt.go b/src/cmd/compile/internal/types/fmt.go index 93061d724d..a42d97cd31 100644 --- a/src/cmd/compile/internal/types/fmt.go +++ b/src/cmd/compile/internal/types/fmt.go @@ -72,6 +72,7 @@ const ( fmtDebug fmtTypeID fmtTypeIDName + fmtTypeIDHash ) // Sym @@ -144,10 +145,21 @@ func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) { if q := pkgqual(s.Pkg, verb, mode); q != "" { b.WriteString(q) b.WriteByte('.') - if mode == fmtTypeIDName { + switch mode { + case fmtTypeIDName: // If name is a generic instantiation, it might have local package placeholders // in it. Replace those placeholders with the package name. See issue 49547. 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) @@ -176,7 +188,7 @@ func pkgqual(pkg *Pkg, verb rune, mode fmtMode) string { case fmtDebug: return pkg.Name - case fmtTypeIDName: + case fmtTypeIDName, fmtTypeIDHash: // dcommontype, typehash 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 { // in %-T mode collapse predeclared aliases with their originals. switch mode { - case fmtTypeIDName, fmtTypeID: + case fmtTypeIDName, fmtTypeIDHash, fmtTypeID: t = Types[t.Kind()] default: 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: b.WriteByte('*') switch mode { - case fmtTypeID, fmtTypeIDName: + case fmtTypeID, fmtTypeIDName, fmtTypeIDHash: if verb == 'S' { tconv2(b, t.Elem(), 'S', mode, visited) return @@ -487,7 +499,7 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type case IsExported(f.Sym.Name): sconv2(b, f.Sym, 'S', mode) default: - if mode != fmtTypeIDName { + if mode != fmtTypeIDName && mode != fmtTypeIDHash { mode = fmtTypeID } 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)) fieldVerb := 'v' switch mode { - case fmtTypeID, fmtTypeIDName, fmtGo: + case fmtTypeID, fmtTypeIDName, fmtTypeIDHash, fmtGo: // no argument names on function signature, and no "noescape"/"nosplit" tags fieldVerb = 'S' } @@ -691,7 +703,7 @@ func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Ty if name == ".F" { 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) } } 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. func TypeHash(t *Type) uint32 { - p := t.NameString() + p := tconv(t, 0, fmtTypeIDHash) // Using MD5 is overkill, but reduces accidental collisions. h := md5.Sum([]byte(p)) diff --git a/test/typeparam/issue51250a.dir/a.go b/test/typeparam/issue51250a.dir/a.go new file mode 100644 index 0000000000..12dd60a3d1 --- /dev/null +++ b/test/typeparam/issue51250a.dir/a.go @@ -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 +} diff --git a/test/typeparam/issue51250a.dir/b.go b/test/typeparam/issue51250a.dir/b.go new file mode 100644 index 0000000000..114c9f80f7 --- /dev/null +++ b/test/typeparam/issue51250a.dir/b.go @@ -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") + } +} diff --git a/test/typeparam/issue51250a.dir/main.go b/test/typeparam/issue51250a.dir/main.go new file mode 100644 index 0000000000..45288be482 --- /dev/null +++ b/test/typeparam/issue51250a.dir/main.go @@ -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]{}) +} diff --git a/test/typeparam/issue51250a.go b/test/typeparam/issue51250a.go new file mode 100644 index 0000000000..aefbe67310 --- /dev/null +++ b/test/typeparam/issue51250a.go @@ -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