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, err := pkgFor(".", test.src, nil)
   111  		if err != nil {
   112  			t.Fatal(err)
   113  		}
   114  
   115  		t.Run(pkg.Name(), func(t *testing.T) {
   116  			ctxt := NewContext()
   117  
   118  			T1 := pkg.Scope().Lookup(test.name1).Type()
   119  			res1, err := Instantiate(ctxt, T1, test.targs1, false)
   120  			if err != nil {
   121  				t.Fatal(err)
   122  			}
   123  
   124  			T2 := pkg.Scope().Lookup(test.name2).Type()
   125  			res2, err := Instantiate(ctxt, T2, test.targs2, false)
   126  			if err != nil {
   127  				t.Fatal(err)
   128  			}
   129  
   130  			if gotEqual := res1 == res2; gotEqual != test.wantEqual {
   131  				t.Errorf("%s == %s: %t, want %t", res1, res2, gotEqual, test.wantEqual)
   132  			}
   133  		})
   134  	}
   135  }
   136  
   137  func TestInstantiateNonEquality(t *testing.T) {
   138  	const src = "package p; type T[P any] int"
   139  	pkg1, err := pkgFor(".", src, nil)
   140  	if err != nil {
   141  		t.Fatal(err)
   142  	}
   143  	pkg2, err := pkgFor(".", src, nil)
   144  	if err != nil {
   145  		t.Fatal(err)
   146  	}
   147  	// We consider T1 and T2 to be distinct types, so their instances should not
   148  	// be deduplicated by the context.
   149  	T1 := pkg1.Scope().Lookup("T").Type().(*Named)
   150  	T2 := pkg2.Scope().Lookup("T").Type().(*Named)
   151  	ctxt := NewContext()
   152  	res1, err := Instantiate(ctxt, T1, []Type{Typ[Int]}, false)
   153  	if err != nil {
   154  		t.Fatal(err)
   155  	}
   156  	res2, err := Instantiate(ctxt, T2, []Type{Typ[Int]}, false)
   157  	if err != nil {
   158  		t.Fatal(err)
   159  	}
   160  	if res1 == res2 {
   161  		t.Errorf("instance from pkg1 (%s) is pointer-equivalent to instance from pkg2 (%s)", res1, res2)
   162  	}
   163  	if Identical(res1, res2) {
   164  		t.Errorf("instance from pkg1 (%s) is identical to instance from pkg2 (%s)", res1, res2)
   165  	}
   166  }
   167  
   168  func TestMethodInstantiation(t *testing.T) {
   169  	const prefix = `package p
   170  
   171  type T[P any] struct{}
   172  
   173  var X T[int]
   174  
   175  `
   176  	tests := []struct {
   177  		decl string
   178  		want string
   179  	}{
   180  		{"func (r T[P]) m() P", "func (T[int]).m() int"},
   181  		{"func (r T[P]) m(P)", "func (T[int]).m(int)"},
   182  		{"func (r *T[P]) m(P)", "func (*T[int]).m(int)"},
   183  		{"func (r T[P]) m() T[P]", "func (T[int]).m() T[int]"},
   184  		{"func (r T[P]) m(T[P])", "func (T[int]).m(T[int])"},
   185  		{"func (r T[P]) m(T[P], P, string)", "func (T[int]).m(T[int], int, string)"},
   186  		{"func (r T[P]) m(T[P], T[string], T[int])", "func (T[int]).m(T[int], T[string], T[int])"},
   187  	}
   188  
   189  	for _, test := range tests {
   190  		src := prefix + test.decl
   191  		pkg, err := pkgFor(".", src, nil)
   192  		if err != nil {
   193  			t.Fatal(err)
   194  		}
   195  		typ := NewPointer(pkg.Scope().Lookup("X").Type())
   196  		obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m")
   197  		m, _ := obj.(*Func)
   198  		if m == nil {
   199  			t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj)
   200  		}
   201  		if got := ObjectString(m, RelativeTo(pkg)); got != test.want {
   202  			t.Errorf("instantiated %q, want %q", got, test.want)
   203  		}
   204  	}
   205  }
   206  
   207  func TestImmutableSignatures(t *testing.T) {
   208  	const src = `package p
   209  
   210  type T[P any] struct{}
   211  
   212  func (T[P]) m() {}
   213  
   214  var _ T[int]
   215  `
   216  	pkg, err := pkgFor(".", src, nil)
   217  	if err != nil {
   218  		t.Fatal(err)
   219  	}
   220  	typ := pkg.Scope().Lookup("T").Type().(*Named)
   221  	obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m")
   222  	if obj == nil {
   223  		t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj)
   224  	}
   225  
   226  	// Verify that the original method is not mutated by instantiating T (this
   227  	// bug manifested when subst did not return a new signature).
   228  	want := "func (T[P]).m()"
   229  	if got := stripAnnotations(ObjectString(obj, RelativeTo(pkg))); got != want {
   230  		t.Errorf("instantiated %q, want %q", got, want)
   231  	}
   232  }
   233  
   234  // Copied from errors.go.
   235  func stripAnnotations(s string) string {
   236  	var b strings.Builder
   237  	for _, r := range s {
   238  		// strip #'s and subscript digits
   239  		if r < '₀' || '₀'+10 <= r { // '₀' == U+2080
   240  			b.WriteRune(r)
   241  		}
   242  	}
   243  	if b.Len() < len(s) {
   244  		return b.String()
   245  	}
   246  	return s
   247  }
   248  

View as plain text