Source file src/go/types/instantiate_test.go

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package types_test
     6  
     7  import (
     8  	"go/token"
     9  	. "go/types"
    10  	"strings"
    11  	"testing"
    12  )
    13  
    14  func TestInstantiateEquality(t *testing.T) {
    15  	emptySignature := NewSignatureType(nil, nil, nil, nil, nil, false)
    16  	tests := []struct {
    17  		src       string
    18  		name1     string
    19  		targs1    []Type
    20  		name2     string
    21  		targs2    []Type
    22  		wantEqual bool
    23  	}{
    24  		{
    25  			"package basictype; type T[P any] int",
    26  			"T", []Type{Typ[Int]},
    27  			"T", []Type{Typ[Int]},
    28  			true,
    29  		},
    30  		{
    31  			"package differenttypeargs; type T[P any] int",
    32  			"T", []Type{Typ[Int]},
    33  			"T", []Type{Typ[String]},
    34  			false,
    35  		},
    36  		{
    37  			"package typeslice; type T[P any] int",
    38  			"T", []Type{NewSlice(Typ[Int])},
    39  			"T", []Type{NewSlice(Typ[Int])},
    40  			true,
    41  		},
    42  		{
    43  			// interface{interface{...}} is equivalent to interface{...}
    44  			"package equivalentinterfaces; type T[P any] int",
    45  			"T", []Type{
    46  				NewInterfaceType([]*Func{NewFunc(token.NoPos, nil, "M", emptySignature)}, nil),
    47  			},
    48  			"T", []Type{
    49  				NewInterfaceType(
    50  					nil,
    51  					[]Type{
    52  						NewInterfaceType([]*Func{NewFunc(token.NoPos, nil, "M", emptySignature)}, nil),
    53  					},
    54  				),
    55  			},
    56  			true,
    57  		},
    58  		{
    59  			// int|string is equivalent to string|int
    60  			"package equivalenttypesets; type T[P any] int",
    61  			"T", []Type{
    62  				NewInterfaceType(nil, []Type{
    63  					NewUnion([]*Term{NewTerm(false, Typ[Int]), NewTerm(false, Typ[String])}),
    64  				}),
    65  			},
    66  			"T", []Type{
    67  				NewInterfaceType(nil, []Type{
    68  					NewUnion([]*Term{NewTerm(false, Typ[String]), NewTerm(false, Typ[Int])}),
    69  				}),
    70  			},
    71  			true,
    72  		},
    73  		{
    74  			"package basicfunc; func F[P any]() {}",
    75  			"F", []Type{Typ[Int]},
    76  			"F", []Type{Typ[Int]},
    77  			true,
    78  		},
    79  		{
    80  			"package funcslice; func F[P any]() {}",
    81  			"F", []Type{NewSlice(Typ[Int])},
    82  			"F", []Type{NewSlice(Typ[Int])},
    83  			true,
    84  		},
    85  		{
    86  			"package funcwithparams; func F[P any](x string) float64 { return 0 }",
    87  			"F", []Type{Typ[Int]},
    88  			"F", []Type{Typ[Int]},
    89  			true,
    90  		},
    91  		{
    92  			"package differentfuncargs; func F[P any](x string) float64 { return 0 }",
    93  			"F", []Type{Typ[Int]},
    94  			"F", []Type{Typ[String]},
    95  			false,
    96  		},
    97  		{
    98  			"package funcequality; func F1[P any](x int) {}; func F2[Q any](x int) {}",
    99  			"F1", []Type{Typ[Int]},
   100  			"F2", []Type{Typ[Int]},
   101  			false,
   102  		},
   103  		{
   104  			"package funcsymmetry; func F1[P any](x P) {}; func F2[Q any](x Q) {}",
   105  			"F1", []Type{Typ[Int]},
   106  			"F2", []Type{Typ[Int]},
   107  			false,
   108  		},
   109  	}
   110  
   111  	for _, test := range tests {
   112  		pkg, err := pkgForMode(".", test.src, nil, 0)
   113  		if err != nil {
   114  			t.Fatal(err)
   115  		}
   116  
   117  		t.Run(pkg.Name(), func(t *testing.T) {
   118  			ctxt := NewContext()
   119  
   120  			T1 := pkg.Scope().Lookup(test.name1).Type()
   121  			res1, err := Instantiate(ctxt, T1, test.targs1, false)
   122  			if err != nil {
   123  				t.Fatal(err)
   124  			}
   125  
   126  			T2 := pkg.Scope().Lookup(test.name2).Type()
   127  			res2, err := Instantiate(ctxt, T2, test.targs2, false)
   128  			if err != nil {
   129  				t.Fatal(err)
   130  			}
   131  
   132  			if gotEqual := res1 == res2; gotEqual != test.wantEqual {
   133  				t.Errorf("%s == %s: %t, want %t", res1, res2, gotEqual, test.wantEqual)
   134  			}
   135  		})
   136  	}
   137  }
   138  
   139  func TestInstantiateNonEquality(t *testing.T) {
   140  	const src = "package p; type T[P any] int"
   141  
   142  	pkg1, err := pkgForMode(".", src, nil, 0)
   143  	if err != nil {
   144  		t.Fatal(err)
   145  	}
   146  	pkg2, err := pkgForMode(".", src, nil, 0)
   147  	if err != nil {
   148  		t.Fatal(err)
   149  	}
   150  
   151  	// We consider T1 and T2 to be distinct types, so their instances should not
   152  	// be deduplicated by the context.
   153  	T1 := pkg1.Scope().Lookup("T").Type().(*Named)
   154  	T2 := pkg2.Scope().Lookup("T").Type().(*Named)
   155  
   156  	ctxt := NewContext()
   157  	res1, err := Instantiate(ctxt, T1, []Type{Typ[Int]}, false)
   158  	if err != nil {
   159  		t.Fatal(err)
   160  	}
   161  	res2, err := Instantiate(ctxt, T2, []Type{Typ[Int]}, false)
   162  	if err != nil {
   163  		t.Fatal(err)
   164  	}
   165  
   166  	if res1 == res2 {
   167  		t.Errorf("instance from pkg1 (%s) is pointer-equivalent to instance from pkg2 (%s)", res1, res2)
   168  	}
   169  	if Identical(res1, res2) {
   170  		t.Errorf("instance from pkg1 (%s) is identical to instance from pkg2 (%s)", res1, res2)
   171  	}
   172  }
   173  
   174  func TestMethodInstantiation(t *testing.T) {
   175  	const prefix = `package p
   176  
   177  type T[P any] struct{}
   178  
   179  var X T[int]
   180  
   181  `
   182  	tests := []struct {
   183  		decl string
   184  		want string
   185  	}{
   186  		{"func (r T[P]) m() P", "func (T[int]).m() int"},
   187  		{"func (r T[P]) m(P)", "func (T[int]).m(int)"},
   188  		{"func (r *T[P]) m(P)", "func (*T[int]).m(int)"},
   189  		{"func (r T[P]) m() T[P]", "func (T[int]).m() T[int]"},
   190  		{"func (r T[P]) m(T[P])", "func (T[int]).m(T[int])"},
   191  		{"func (r T[P]) m(T[P], P, string)", "func (T[int]).m(T[int], int, string)"},
   192  		{"func (r T[P]) m(T[P], T[string], T[int])", "func (T[int]).m(T[int], T[string], T[int])"},
   193  	}
   194  
   195  	for _, test := range tests {
   196  		src := prefix + test.decl
   197  		pkg, err := pkgForMode(".", src, nil, 0)
   198  		if err != nil {
   199  			t.Fatal(err)
   200  		}
   201  		typ := NewPointer(pkg.Scope().Lookup("X").Type())
   202  		obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m")
   203  		m, _ := obj.(*Func)
   204  		if m == nil {
   205  			t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj)
   206  		}
   207  		if got := ObjectString(m, RelativeTo(pkg)); got != test.want {
   208  			t.Errorf("instantiated %q, want %q", got, test.want)
   209  		}
   210  	}
   211  }
   212  
   213  func TestImmutableSignatures(t *testing.T) {
   214  	const src = `package p
   215  
   216  type T[P any] struct{}
   217  
   218  func (T[P]) m() {}
   219  
   220  var _ T[int]
   221  `
   222  	pkg, err := pkgForMode(".", src, nil, 0)
   223  	if err != nil {
   224  		t.Fatal(err)
   225  	}
   226  	typ := pkg.Scope().Lookup("T").Type().(*Named)
   227  	obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m")
   228  	if obj == nil {
   229  		t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj)
   230  	}
   231  
   232  	// Verify that the original method is not mutated by instantiating T (this
   233  	// bug manifested when subst did not return a new signature).
   234  	want := "func (T[P]).m()"
   235  	if got := stripAnnotations(ObjectString(obj, RelativeTo(pkg))); got != want {
   236  		t.Errorf("instantiated %q, want %q", got, want)
   237  	}
   238  }
   239  
   240  // Copied from errors.go.
   241  func stripAnnotations(s string) string {
   242  	var b strings.Builder
   243  	for _, r := range s {
   244  		// strip #'s and subscript digits
   245  		if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
   246  			b.WriteRune(r)
   247  		}
   248  	}
   249  	if b.Len() < len(s) {
   250  		return b.String()
   251  	}
   252  	return s
   253  }
   254  

View as plain text