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

View as plain text