mirror of
https://github.com/golang/go.git
synced 2025-05-17 13:24:38 +00:00
go/types: ObjectString: remove only 1 Alias for "type A = RHS"
As we migrate towards materialized Alias types, the ObjectString for a type A such as type A = B type B = int should be "type A = B", removing exactly one Alias constructor from the type of A. (The previous behavior was "type A = int".) I suspect the existing Alias.{Unalias,Underlying} API is inadequate and that we will need an Alias.RHS accessor that removes exactly one Alias. Other clients such as the import/ export packages will need it, because aliases are not isomorphic to defined types, in which, given type A B type B int the Underlying of A is indeed int. See #66559. Change-Id: I11a4aacbe6dbeeafc3aee31b3c096296b5970cd8 Reviewed-on: https://go-review.googlesource.com/c/go/+/574716 Auto-Submit: Alan Donovan <adonovan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
parent
6ffbcd8249
commit
61a3ee5441
@ -542,10 +542,14 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
|||||||
}
|
}
|
||||||
if tname.IsAlias() {
|
if tname.IsAlias() {
|
||||||
buf.WriteString(" =")
|
buf.WriteString(" =")
|
||||||
|
if alias, ok := typ.(*Alias); ok { // materialized? (gotypesalias=1)
|
||||||
|
typ = alias.fromRHS
|
||||||
|
}
|
||||||
} else if t, _ := typ.(*TypeParam); t != nil {
|
} else if t, _ := typ.(*TypeParam); t != nil {
|
||||||
typ = t.bound
|
typ = t.bound
|
||||||
} else {
|
} else {
|
||||||
// TODO(gri) should this be fromRHS for *Named?
|
// TODO(gri) should this be fromRHS for *Named?
|
||||||
|
// (See discussion in #66559.)
|
||||||
typ = under(typ)
|
typ = under(typ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package types2_test
|
package types2_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"internal/testenv"
|
"internal/testenv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -79,69 +80,72 @@ func TestEmbeddedMethod(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var testObjects = []struct {
|
var testObjects = []struct {
|
||||||
src string
|
src string
|
||||||
obj string
|
obj string
|
||||||
want string
|
want string
|
||||||
|
alias bool // needs materialized aliases
|
||||||
}{
|
}{
|
||||||
{"import \"io\"; var r io.Reader", "r", "var p.r io.Reader"},
|
{"import \"io\"; var r io.Reader", "r", "var p.r io.Reader", false},
|
||||||
|
|
||||||
{"const c = 1.2", "c", "const p.c untyped float"},
|
{"const c = 1.2", "c", "const p.c untyped float", false},
|
||||||
{"const c float64 = 3.14", "c", "const p.c float64"},
|
{"const c float64 = 3.14", "c", "const p.c float64", false},
|
||||||
|
|
||||||
{"type t struct{f int}", "t", "type p.t struct{f int}"},
|
{"type t struct{f int}", "t", "type p.t struct{f int}", false},
|
||||||
{"type t func(int)", "t", "type p.t func(int)"},
|
{"type t func(int)", "t", "type p.t func(int)", false},
|
||||||
{"type t[P any] struct{f P}", "t", "type p.t[P any] struct{f P}"},
|
{"type t[P any] struct{f P}", "t", "type p.t[P any] struct{f P}", false},
|
||||||
{"type t[P any] struct{f P}", "t.P", "type parameter P any"},
|
{"type t[P any] struct{f P}", "t.P", "type parameter P any", false},
|
||||||
{"type C interface{m()}; type t[P C] struct{}", "t.P", "type parameter P p.C"},
|
{"type C interface{m()}; type t[P C] struct{}", "t.P", "type parameter P p.C", false},
|
||||||
|
|
||||||
{"type t = struct{f int}", "t", "type p.t = struct{f int}"},
|
{"type t = struct{f int}", "t", "type p.t = struct{f int}", false},
|
||||||
{"type t = func(int)", "t", "type p.t = func(int)"},
|
{"type t = func(int)", "t", "type p.t = func(int)", false},
|
||||||
|
{"type A = B; type B = int", "A", "type p.A = p.B", true},
|
||||||
|
|
||||||
{"var v int", "v", "var p.v int"},
|
{"var v int", "v", "var p.v int", false},
|
||||||
|
|
||||||
{"func f(int) string", "f", "func p.f(int) string"},
|
{"func f(int) string", "f", "func p.f(int) string", false},
|
||||||
{"func g[P any](x P){}", "g", "func p.g[P any](x P)"},
|
{"func g[P any](x P){}", "g", "func p.g[P any](x P)", false},
|
||||||
{"func g[P interface{~int}](x P){}", "g.P", "type parameter P interface{~int}"},
|
{"func g[P interface{~int}](x P){}", "g.P", "type parameter P interface{~int}", false},
|
||||||
{"", "any", "type any = interface{}"},
|
{"", "any", "type any = interface{}", false},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObjectString(t *testing.T) {
|
func TestObjectString(t *testing.T) {
|
||||||
testenv.MustHaveGoBuild(t)
|
testenv.MustHaveGoBuild(t)
|
||||||
|
|
||||||
for _, test := range testObjects {
|
for i, test := range testObjects {
|
||||||
src := "package p; " + test.src
|
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||||
pkg, err := typecheck(src, nil, nil)
|
if test.alias {
|
||||||
if err != nil {
|
t.Setenv("GODEBUG", "gotypesalias=1")
|
||||||
t.Errorf("%s: %s", src, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
names := strings.Split(test.obj, ".")
|
|
||||||
if len(names) != 1 && len(names) != 2 {
|
|
||||||
t.Errorf("%s: invalid object path %s", test.src, test.obj)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, obj := pkg.Scope().LookupParent(names[0], nopos)
|
|
||||||
if obj == nil {
|
|
||||||
t.Errorf("%s: %s not found", test.src, names[0])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(names) == 2 {
|
|
||||||
if typ, ok := obj.Type().(interface{ TypeParams() *TypeParamList }); ok {
|
|
||||||
obj = lookupTypeParamObj(typ.TypeParams(), names[1])
|
|
||||||
if obj == nil {
|
|
||||||
t.Errorf("%s: %s not found", test.src, test.obj)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Errorf("%s: %s has no type parameters", test.src, names[0])
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if got := obj.String(); got != test.want {
|
src := "package p; " + test.src
|
||||||
t.Errorf("%s: got %s, want %s", test.src, got, test.want)
|
pkg, err := typecheck(src, nil, nil)
|
||||||
}
|
if err != nil {
|
||||||
|
t.Fatalf("%s: %s", src, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
names := strings.Split(test.obj, ".")
|
||||||
|
if len(names) != 1 && len(names) != 2 {
|
||||||
|
t.Fatalf("%s: invalid object path %s", test.src, test.obj)
|
||||||
|
}
|
||||||
|
_, obj := pkg.Scope().LookupParent(names[0], nopos)
|
||||||
|
if obj == nil {
|
||||||
|
t.Fatalf("%s: %s not found", test.src, names[0])
|
||||||
|
}
|
||||||
|
if len(names) == 2 {
|
||||||
|
if typ, ok := obj.Type().(interface{ TypeParams() *TypeParamList }); ok {
|
||||||
|
obj = lookupTypeParamObj(typ.TypeParams(), names[1])
|
||||||
|
if obj == nil {
|
||||||
|
t.Fatalf("%s: %s not found", test.src, test.obj)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Fatalf("%s: %s has no type parameters", test.src, names[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := obj.String(); got != test.want {
|
||||||
|
t.Errorf("%s: got %s, want %s", test.src, got, test.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,10 +545,14 @@ func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
|
|||||||
}
|
}
|
||||||
if tname.IsAlias() {
|
if tname.IsAlias() {
|
||||||
buf.WriteString(" =")
|
buf.WriteString(" =")
|
||||||
|
if alias, ok := typ.(*Alias); ok { // materialized? (gotypesalias=1)
|
||||||
|
typ = alias.fromRHS
|
||||||
|
}
|
||||||
} else if t, _ := typ.(*TypeParam); t != nil {
|
} else if t, _ := typ.(*TypeParam); t != nil {
|
||||||
typ = t.bound
|
typ = t.bound
|
||||||
} else {
|
} else {
|
||||||
// TODO(gri) should this be fromRHS for *Named?
|
// TODO(gri) should this be fromRHS for *Named?
|
||||||
|
// (See discussion in #66559.)
|
||||||
typ = under(typ)
|
typ = under(typ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
package types_test
|
package types_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"internal/testenv"
|
"internal/testenv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -82,69 +83,72 @@ func TestEmbeddedMethod(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var testObjects = []struct {
|
var testObjects = []struct {
|
||||||
src string
|
src string
|
||||||
obj string
|
obj string
|
||||||
want string
|
want string
|
||||||
|
alias bool // needs materialized aliases
|
||||||
}{
|
}{
|
||||||
{"import \"io\"; var r io.Reader", "r", "var p.r io.Reader"},
|
{"import \"io\"; var r io.Reader", "r", "var p.r io.Reader", false},
|
||||||
|
|
||||||
{"const c = 1.2", "c", "const p.c untyped float"},
|
{"const c = 1.2", "c", "const p.c untyped float", false},
|
||||||
{"const c float64 = 3.14", "c", "const p.c float64"},
|
{"const c float64 = 3.14", "c", "const p.c float64", false},
|
||||||
|
|
||||||
{"type t struct{f int}", "t", "type p.t struct{f int}"},
|
{"type t struct{f int}", "t", "type p.t struct{f int}", false},
|
||||||
{"type t func(int)", "t", "type p.t func(int)"},
|
{"type t func(int)", "t", "type p.t func(int)", false},
|
||||||
{"type t[P any] struct{f P}", "t", "type p.t[P any] struct{f P}"},
|
{"type t[P any] struct{f P}", "t", "type p.t[P any] struct{f P}", false},
|
||||||
{"type t[P any] struct{f P}", "t.P", "type parameter P any"},
|
{"type t[P any] struct{f P}", "t.P", "type parameter P any", false},
|
||||||
{"type C interface{m()}; type t[P C] struct{}", "t.P", "type parameter P p.C"},
|
{"type C interface{m()}; type t[P C] struct{}", "t.P", "type parameter P p.C", false},
|
||||||
|
|
||||||
{"type t = struct{f int}", "t", "type p.t = struct{f int}"},
|
{"type t = struct{f int}", "t", "type p.t = struct{f int}", false},
|
||||||
{"type t = func(int)", "t", "type p.t = func(int)"},
|
{"type t = func(int)", "t", "type p.t = func(int)", false},
|
||||||
|
{"type A = B; type B = int", "A", "type p.A = p.B", true},
|
||||||
|
|
||||||
{"var v int", "v", "var p.v int"},
|
{"var v int", "v", "var p.v int", false},
|
||||||
|
|
||||||
{"func f(int) string", "f", "func p.f(int) string"},
|
{"func f(int) string", "f", "func p.f(int) string", false},
|
||||||
{"func g[P any](x P){}", "g", "func p.g[P any](x P)"},
|
{"func g[P any](x P){}", "g", "func p.g[P any](x P)", false},
|
||||||
{"func g[P interface{~int}](x P){}", "g.P", "type parameter P interface{~int}"},
|
{"func g[P interface{~int}](x P){}", "g.P", "type parameter P interface{~int}", false},
|
||||||
{"", "any", "type any = interface{}"},
|
{"", "any", "type any = interface{}", false},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestObjectString(t *testing.T) {
|
func TestObjectString(t *testing.T) {
|
||||||
testenv.MustHaveGoBuild(t)
|
testenv.MustHaveGoBuild(t)
|
||||||
|
|
||||||
for _, test := range testObjects {
|
for i, test := range testObjects {
|
||||||
src := "package p; " + test.src
|
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||||
pkg, err := typecheck(src, nil, nil)
|
if test.alias {
|
||||||
if err != nil {
|
t.Setenv("GODEBUG", "gotypesalias=1")
|
||||||
t.Errorf("%s: %s", src, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
names := strings.Split(test.obj, ".")
|
|
||||||
if len(names) != 1 && len(names) != 2 {
|
|
||||||
t.Errorf("%s: invalid object path %s", test.src, test.obj)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, obj := pkg.Scope().LookupParent(names[0], nopos)
|
|
||||||
if obj == nil {
|
|
||||||
t.Errorf("%s: %s not found", test.src, names[0])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(names) == 2 {
|
|
||||||
if typ, ok := obj.Type().(interface{ TypeParams() *TypeParamList }); ok {
|
|
||||||
obj = lookupTypeParamObj(typ.TypeParams(), names[1])
|
|
||||||
if obj == nil {
|
|
||||||
t.Errorf("%s: %s not found", test.src, test.obj)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Errorf("%s: %s has no type parameters", test.src, names[0])
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if got := obj.String(); got != test.want {
|
src := "package p; " + test.src
|
||||||
t.Errorf("%s: got %s, want %s", test.src, got, test.want)
|
pkg, err := typecheck(src, nil, nil)
|
||||||
}
|
if err != nil {
|
||||||
|
t.Fatalf("%s: %s", src, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
names := strings.Split(test.obj, ".")
|
||||||
|
if len(names) != 1 && len(names) != 2 {
|
||||||
|
t.Fatalf("%s: invalid object path %s", test.src, test.obj)
|
||||||
|
}
|
||||||
|
_, obj := pkg.Scope().LookupParent(names[0], nopos)
|
||||||
|
if obj == nil {
|
||||||
|
t.Fatalf("%s: %s not found", test.src, names[0])
|
||||||
|
}
|
||||||
|
if len(names) == 2 {
|
||||||
|
if typ, ok := obj.Type().(interface{ TypeParams() *TypeParamList }); ok {
|
||||||
|
obj = lookupTypeParamObj(typ.TypeParams(), names[1])
|
||||||
|
if obj == nil {
|
||||||
|
t.Fatalf("%s: %s not found", test.src, test.obj)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Fatalf("%s: %s has no type parameters", test.src, names[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := obj.String(); got != test.want {
|
||||||
|
t.Errorf("%s: got %s, want %s", test.src, got, test.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user