Skip to content

Commit aa6a7fa

Browse files
committed
cmd/compile: fix reflect naming of local generic types
To disambiguate local types, we append a "·N" suffix to their name and then trim it off again when producing their runtime type descriptors. However, if a local type is generic, then we were further appending the type arguments after this suffix, and the code in types/fmt.go responsible for trimming didn't know to handle this. We could extend the types/fmt.go code to look for the "·N" suffix elsewhere in the type name, but this is risky because it could legitimately (albeit unlikely) appear in struct field tags. Instead, the most robust solution is to just change the mangling logic to keep the "·N" suffix at the end, where types/fmt.go can easily and reliably trim it. Note: the "·N" suffix is still visible within the type arguments list (e.g., the "·3" suffixes in nested.out), because we currently use the link strings in the type arguments list. Fixes #54456. Change-Id: Ie9beaf7e5330982f539bff57b8d48868a3674a37 Reviewed-on: https://go-review.googlesource.com/c/go/+/424901 TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Matthew Dempsky <[email protected]> Reviewed-by: Cuong Manh Le <[email protected]> Reviewed-by: Than McIntosh <[email protected]>
1 parent 72a76ca commit aa6a7fa

File tree

6 files changed

+72
-14
lines changed

6 files changed

+72
-14
lines changed

src/cmd/compile/internal/noder/reader.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -777,8 +777,13 @@ func (dict *readerDict) mangle(sym *types.Sym) *types.Sym {
777777
return sym
778778
}
779779

780+
// If sym is a locally defined generic type, we need the suffix to
781+
// stay at the end after mangling so that types/fmt.go can strip it
782+
// out again when writing the type's runtime descriptor (#54456).
783+
base, suffix := types.SplitVargenSuffix(sym.Name)
784+
780785
var buf strings.Builder
781-
buf.WriteString(sym.Name)
786+
buf.WriteString(base)
782787
buf.WriteByte('[')
783788
for i, targ := range dict.targs {
784789
if i > 0 {
@@ -791,6 +796,7 @@ func (dict *readerDict) mangle(sym *types.Sym) *types.Sym {
791796
buf.WriteString(targ.LinkString())
792797
}
793798
buf.WriteByte(']')
799+
buf.WriteString(suffix)
794800
return sym.Pkg.Lookup(buf.String())
795801
}
796802

src/cmd/compile/internal/noder/writer.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -933,8 +933,11 @@ func (w *writer) qualifiedIdent(obj types2.Object) {
933933
decl, ok := w.p.typDecls[obj.(*types2.TypeName)]
934934
assert(ok)
935935
if decl.gen != 0 {
936-
// TODO(mdempsky): Find a better solution than embedding middle
937-
// dot in the symbol name; this is terrible.
936+
// For local defined types, we embed a scope-disambiguation
937+
// number directly into their name. types.SplitVargenSuffix then
938+
// knows to look for this.
939+
//
940+
// TODO(mdempsky): Find a better solution; this is terrible.
938941
name = fmt.Sprintf("%s·%v", name, decl.gen)
939942
}
940943
}

src/cmd/compile/internal/types/fmt.go

+18-7
Original file line numberDiff line numberDiff line change
@@ -340,13 +340,9 @@ func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type
340340
// non-fmtTypeID modes.
341341
sym := t.Sym()
342342
if mode != fmtTypeID {
343-
i := len(sym.Name)
344-
for i > 0 && sym.Name[i-1] >= '0' && sym.Name[i-1] <= '9' {
345-
i--
346-
}
347-
const dot = "·"
348-
if i >= len(dot) && sym.Name[i-len(dot):i] == dot {
349-
sym = &Sym{Pkg: sym.Pkg, Name: sym.Name[:i-len(dot)]}
343+
base, _ := SplitVargenSuffix(sym.Name)
344+
if len(base) < len(sym.Name) {
345+
sym = &Sym{Pkg: sym.Pkg, Name: base}
350346
}
351347
}
352348
sconv2(b, sym, verb, mode)
@@ -704,6 +700,21 @@ func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Ty
704700
}
705701
}
706702

703+
// SplitVargenSuffix returns name split into a base string and a ·N
704+
// suffix, if any.
705+
func SplitVargenSuffix(name string) (base, suffix string) {
706+
i := len(name)
707+
for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' {
708+
i--
709+
}
710+
const dot = "·"
711+
if i >= len(dot) && name[i-len(dot):i] == dot {
712+
i -= len(dot)
713+
return name[:i], name[i:]
714+
}
715+
return name, ""
716+
}
717+
707718
// Val
708719

709720
func FmtConst(v constant.Value, sharp bool) string {

test/run.go

+1
Original file line numberDiff line numberDiff line change
@@ -1990,6 +1990,7 @@ var go118Failures = setOf(
19901990
"fixedbugs/issue54343.go", // 1.18 compiler assigns receiver parameter to global variable
19911991
"typeparam/nested.go", // 1.18 compiler doesn't support function-local types with generics
19921992
"typeparam/issue51521.go", // 1.18 compiler produces bad panic message and link error
1993+
"typeparam/issue54456.go", // 1.18 compiler fails to distinguish local generic types
19931994
"typeparam/issue54497.go", // 1.18 compiler is more conservative about inlining due to repeated issues
19941995
"typeparam/mdempsky/16.go", // 1.18 compiler uses interface shape type in failed type assertions
19951996
"typeparam/mdempsky/17.go", // 1.18 compiler mishandles implicit conversions from range loops

test/typeparam/issue54456.go

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// run
2+
3+
// Copyright 2022 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+
// The Go 1.18 frontend failed to disambiguate instantiations of
8+
// different, locally defined generic types with the same name.
9+
//
10+
// The unified frontend also exposed the scope-disambiguation mangling
11+
// to end users in reflect data.
12+
13+
package main
14+
15+
import (
16+
"reflect"
17+
)
18+
19+
func one() any { type T[_ any] int; return T[int](0) }
20+
func two() any { type T[_ any] int; return T[int](0) }
21+
22+
func main() {
23+
p, q := one(), two()
24+
25+
// p and q have different dynamic types; this comparison should
26+
// evaluate false.
27+
if p == q {
28+
panic("bad type identity")
29+
}
30+
31+
for _, x := range []any{p, q} {
32+
// The names here should not contain "·1" or "·2".
33+
if name := reflect.TypeOf(x).String(); name != "main.T[int]" {
34+
panic(name)
35+
}
36+
}
37+
}

test/typeparam/nested.out

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
0,3: main.T·2[int;int]
2-
4,7: main.T·2[int;main.U·3[int;int]]
3-
22,23: main.T·2[main.Int;main.Int]
4-
26,27: main.T·2[main.Int;main.U·3[main.Int;main.Int]]
1+
0,3: main.T[int;int]
2+
4,7: main.T[int;main.U[int;int]·3]
3+
22,23: main.T[main.Int;main.Int]
4+
26,27: main.T[main.Int;main.U[main.Int;main.Int]·3]

0 commit comments

Comments
 (0)