import (
"cmd/compile/internal/syntax"
+ "cmd/compile/internal/typecheck"
"cmd/compile/internal/types2"
"encoding/binary"
"fmt"
if r.p.exportVersion < iexportVersionGenerics {
errorf("unexpected type param type")
}
- // Remove the "path" from the type param name that makes it unique
- ix := strings.LastIndex(name, ".")
- if ix < 0 {
- errorf("missing path for type param")
+ name0 := typecheck.TparamName(name)
+ if name0 == "" {
+ errorf("malformed type parameter export name %s: missing prefix", name)
}
- tn := types2.NewTypeName(pos, r.currPkg, name[ix+1:], nil)
+
+ tn := types2.NewTypeName(pos, r.currPkg, name0, nil)
t := types2.NewTypeParam(tn, nil)
// To handle recursive references to the typeparam within its
// bound, save the partial type in tparamIndex before reading the bounds.
// Save the name of the type parameter in the sym of the type.
// Include the types2 subscript in the sym name
pkg := g.tpkg(typ)
- // Create the unique types1 name for a type param, using its context with a
- // function, type, or method declaration.
+ // Create the unique types1 name for a type param, using its context
+ // with a function, type, or method declaration. Also, map blank type
+ // param names to a unique name based on their type param index. The
+ // unique blank names will be exported, but will be reverted during
+ // types2 and gcimporter import.
assert(g.curDecl != "")
- nm := g.curDecl + "." + typ.Obj().Name()
+ nm := typecheck.TparamExportName(g.curDecl, typ.Obj().Name(), typ.Index())
sym := pkg.Lookup(nm)
if sym.Def != nil {
// Make sure we use the same type param type for the same
"io"
"math/big"
"sort"
+ "strconv"
"strings"
"cmd/compile/internal/base"
w.pkg(s.Pkg)
}
+const blankMarker = "$"
+
+// TparamExportName creates a unique name for type param in a method or a generic
+// type, using the specified unique prefix and the index of the type param. The index
+// is only used if the type param is blank, in which case the blank is replace by
+// "$<index>". A unique name is needed for later substitution in the compiler and
+// export/import that keeps blank type params associated with the correct constraint.
+func TparamExportName(prefix string, name string, index int) string {
+ if name == "_" {
+ name = blankMarker + strconv.Itoa(index)
+ }
+ return prefix + "." + name
+}
+
+// TparamName returns the real name of a type parameter, after stripping its
+// qualifying prefix and reverting blank-name encoding. See TparamExportName
+// for details.
+func TparamName(exportName string) string {
+ // Remove the "path" from the type param name that makes it unique.
+ ix := strings.LastIndex(exportName, ".")
+ if ix < 0 {
+ return ""
+ }
+ name := exportName[ix+1:]
+ if strings.HasPrefix(name, blankMarker) {
+ return "_"
+ }
+ return name
+}
+
func (w *exportWriter) selector(s *types.Sym) {
if w.currPkg == nil {
base.Fatalf("missing currPkg")
// Declare type parameters up-front.
// The scope of type parameters starts at the beginning of the type parameter
// list (so we can have mutually recursive parameterized type bounds).
- nblanks := 0
for i, f := range list {
tparams[i] = check.declareTypeParam(f.Name)
- // Issue #50481: For now, disallow multiple blank type parameters because
- // it causes problems with export data. Report an error unless we are in
- // testing mode ("assert" is defined).
- // We expect to lift this restriction for Go 1.19.
- if f.Name.Value == "_" {
- nblanks++
- if nblanks == 2 && Universe.Lookup("assert") == nil {
- check.softErrorf(f, "cannot have multiple blank type parameters (temporary restriction, see issue #50481)")
- }
- }
}
// Set the type parameters before collecting the type constraints because
if r.p.exportVersion < iexportVersionGenerics {
errorf("unexpected type param type")
}
- // Remove the "path" from the type param name that makes it unique
- ix := strings.LastIndex(name, ".")
- if ix < 0 {
- errorf("missing path for type param")
- }
- tn := types.NewTypeName(pos, r.currPkg, name[ix+1:], nil)
+ // Remove the "path" from the type param name that makes it unique,
+ // and revert any unique name used for blank typeparams.
+ name0 := tparamName(name)
+ tn := types.NewTypeName(pos, r.currPkg, name0, nil)
t := types.NewTypeParam(tn, nil)
// To handle recursive references to the typeparam within its
// bound, save the partial type in tparamIndex before reading the bounds.
n, _ := typ.(*types.Named)
return n
}
+
+const blankMarker = "$"
+
+// tparamName returns the real name of a type parameter, after stripping its
+// qualifying prefix and reverting blank-name encoding. See tparamExportName
+// for details.
+func tparamName(exportName string) string {
+ // Remove the "path" from the type param name that makes it unique.
+ ix := strings.LastIndex(exportName, ".")
+ if ix < 0 {
+ errorf("malformed type parameter export name %s: missing prefix", exportName)
+ }
+ name := exportName[ix+1:]
+ if strings.HasPrefix(name, blankMarker) {
+ return "_"
+ }
+ return name
+}
// Declare type parameters up-front, with empty interface as type bound.
// The scope of type parameters starts at the beginning of the type parameter
// list (so we can have mutually recursive parameterized interfaces).
- nblanks := 0
for _, f := range list.List {
tparams = check.declareTypeParams(tparams, f.Names)
- // Issue #50481: For now, disallow multiple blank type parameters because
- // it causes problems with export data. Report an error unless we are in
- // testing mode ("assert" is defined).
- // We expect to lift this restriction for Go 1.19.
- for _, name := range f.Names {
- if name.Name == "_" {
- nblanks++
- if nblanks == 2 && Universe.Lookup("assert") == nil {
- check.softErrorf(name, _InvalidBlank, "cannot have multiple blank type parameters (temporary restriction, see issue #50481)")
- }
- }
- }
}
// Set the type parameters before collecting the type constraints because
--- /dev/null
+// run -gcflags=-G=3
+
+// 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.
+
+// Test that type substitution works correctly even for a method of a generic type
+// that has multiple blank type params.
+
+package main
+
+import (
+ "fmt"
+)
+
+func main() {
+ foo := &Foo[string, int]{
+ valueA: "i am a string",
+ valueB: 123,
+ }
+ if got, want := fmt.Sprintln(foo), "i am a string 123\n"; got != want {
+ panic(fmt.Sprintf("got %s, want %s", got, want))
+ }
+}
+
+type Foo[T1 any, T2 any] struct {
+ valueA T1
+ valueB T2
+}
+
+func (f *Foo[_, _]) String() string {
+ return fmt.Sprintf("%v %v", f.valueA, f.valueB)
+}
+++ /dev/null
-// errorcheck -G=3
-
-// 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 p
-
-type _[_ any] struct{}
-type _[_, _ any] struct{} // ERROR "cannot have multiple blank type parameters"
-type _[_, _, _ any] struct{} // ERROR "cannot have multiple blank type parameters"
-type _[a, _, b, _, c, _ any] struct{} // ERROR "cannot have multiple blank type parameters"
-
-func _[_ any]() {}
-func _[_, _ any]() {} // ERROR "cannot have multiple blank type parameters"
-func _[_, _, _ any]() {} // ERROR "cannot have multiple blank type parameters"
-func _[a, _, b, _, c, _ any]() {} // ERROR "cannot have multiple blank type parameters"
-
-type S[P1, P2 any] struct{}
-
-func (_ S[_, _]) m() {} // this is ok
--- /dev/null
+// 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 "fmt"
+
+type Foo[T1 ~string, T2 ~int] struct {
+ ValueA T1
+ ValueB T2
+}
+
+func (f *Foo[_, _]) String() string {
+ return fmt.Sprintf("%v %v", f.ValueA, f.ValueB)
+}
--- /dev/null
+// 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.
+
+// Test that type substitution and export/import works correctly even for a method of
+// a generic type that has multiple blank type params.
+
+package main
+
+import (
+ "b"
+ "fmt"
+)
+
+func main() {
+ foo := &b.Foo[string, int]{
+ ValueA: "i am a string",
+ ValueB: 123,
+ }
+ if got, want := fmt.Sprintln(foo), "i am a string 123\n"; got != want {
+ panic(fmt.Sprintf("got %s, want %s", got, want))
+ }
+}
--- /dev/null
+// rundir -G=3
+
+// 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
--- /dev/null
+// 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 A interface {
+ int | int64
+}
+
+type B interface {
+ string
+}
+
+type C interface {
+ String() string
+}
+
+type Myint int
+
+func (i Myint) String() string {
+ return "aa"
+}
+
+type T[P A, _ C, _ B] int
+
+func (v T[P, Q, R]) test() {
+ var r Q
+ r.String()
+}
--- /dev/null
+// 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.
+
+// Test that type substitution works and export/import works correctly even for a
+// generic type that has multiple blank type params.
+
+package main
+
+import (
+ "a"
+ "fmt"
+)
+
+func main() {
+ var x a.T[int, a.Myint, string]
+ fmt.Printf("%v\n", x)
+}
--- /dev/null
+// rundir -G=3
+
+// 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