github.com/goplus/igop@v0.25.0/typeparam_test.go (about)

     1  //go:build go1.18
     2  // +build go1.18
     3  
     4  /*
     5   * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved.
     6   *
     7   * Licensed under the Apache License, Version 2.0 (the "License");
     8   * you may not use this file except in compliance with the License.
     9   * You may obtain a copy of the License at
    10   *
    11   *     http://www.apache.org/licenses/LICENSE-2.0
    12   *
    13   * Unless required by applicable law or agreed to in writing, software
    14   * distributed under the License is distributed on an "AS IS" BASIS,
    15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16   * See the License for the specific language governing permissions and
    17   * limitations under the License.
    18   */
    19  
    20  package igop_test
    21  
    22  import (
    23  	"bytes"
    24  	"fmt"
    25  	"runtime"
    26  	"testing"
    27  
    28  	"github.com/goplus/igop"
    29  	_ "github.com/goplus/igop/pkg/go/token"
    30  	_ "github.com/goplus/igop/pkg/path/filepath"
    31  	_ "github.com/goplus/igop/pkg/reflect"
    32  	_ "github.com/goplus/igop/pkg/sync/atomic"
    33  )
    34  
    35  func TestTypeParamNamed(t *testing.T) {
    36  	src := `package main
    37  
    38  import (
    39  	"path/filepath"
    40  	"reflect"
    41  )
    42  
    43  type N int
    44  type _Nil[a any, b any] struct{}
    45  
    46  func main() {
    47  	var v1 _Nil[int, N]
    48  	var v2 _Nil[filepath.WalkFunc, _Nil[int, N]]
    49  	if s := reflect.TypeOf(v1).String(); s != "main._Nil[int,main.N]" {
    50  		panic(s)
    51  	}
    52  	if s := reflect.TypeOf(v2).String(); s != "main._Nil[path/filepath.WalkFunc,main._Nil[int,main.N]]" {
    53  		panic(s)
    54  	}
    55  }
    56  `
    57  	_, err := igop.RunFile("main.go", src, nil, 0)
    58  	if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  }
    62  
    63  func TestNestedTypeParameterized(t *testing.T) {
    64  	src := `// run
    65  
    66  // Copyright 2021 The Go Authors. All rights reserved.
    67  // Use of this source code is governed by a BSD-style
    68  // license that can be found in the LICENSE file.
    69  
    70  // This test case stress tests a number of subtle cases involving
    71  // nested type-parameterized declarations. At a high-level, it
    72  // declares a generic function that contains a generic type
    73  // declaration:
    74  //
    75  //	func F[A intish]() {
    76  //		type T[B intish] struct{}
    77  //
    78  //		// store reflect.Type tuple (A, B, F[A].T[B]) in tests
    79  //	}
    80  //
    81  // It then instantiates this function with a variety of type arguments
    82  // for A and B. Particularly tricky things like shadowed types.
    83  //
    84  // From this data it tests two things:
    85  //
    86  // 1. Given tuples (A, B, F[A].T[B]) and (A', B', F[A'].T[B']),
    87  //    F[A].T[B] should be identical to F[A'].T[B'] iff (A, B) is
    88  //    identical to (A', B').
    89  //
    90  // 2. A few of the instantiations are constructed to be identical, and
    91  //    it tests that exactly these pairs are duplicated (by golden
    92  //    output comparison to nested.out).
    93  //
    94  // In both cases, we're effectively using the compiler's existing
    95  // runtime.Type handling (which is well tested) of type identity of A
    96  // and B as a way to help bootstrap testing and validate its new
    97  // runtime.Type handling of F[A].T[B].
    98  //
    99  // This isn't perfect, but it smoked out a handful of issues in
   100  // gotypes2 and unified IR.
   101  
   102  package main
   103  
   104  import (
   105  	"fmt"
   106  	"reflect"
   107  )
   108  
   109  type test struct {
   110  	TArgs    [2]reflect.Type
   111  	Instance reflect.Type
   112  }
   113  
   114  var tests []test
   115  
   116  type intish interface{ ~int }
   117  
   118  type Int int
   119  type GlobalInt = Int // allow access to global Int, even when shadowed
   120  
   121  func F[A intish]() {
   122  	add := func(B, T interface{}) {
   123  		tests = append(tests, test{
   124  			TArgs: [2]reflect.Type{
   125  				reflect.TypeOf(A(0)),
   126  				reflect.TypeOf(B),
   127  			},
   128  			Instance: reflect.TypeOf(T),
   129  		})
   130  	}
   131  
   132  	type Int int
   133  
   134  	type T[B intish] struct{}
   135  
   136  	add(int(0), T[int]{})
   137  	add(Int(0), T[Int]{})
   138  	add(GlobalInt(0), T[GlobalInt]{})
   139  	add(A(0), T[A]{}) // NOTE: intentionally dups with int and GlobalInt
   140  
   141  	type U[_ any] int
   142  	type V U[int]
   143  	type W V
   144  
   145  	add(U[int](0), T[U[int]]{})
   146  	add(U[Int](0), T[U[Int]]{})
   147  	add(U[GlobalInt](0), T[U[GlobalInt]]{})
   148  	add(U[A](0), T[U[A]]{}) // NOTE: intentionally dups with U[int] and U[GlobalInt]
   149  	add(V(0), T[V]{})
   150  	add(W(0), T[W]{})
   151  }
   152  
   153  func main() {
   154  	type Int int
   155  
   156  	F[int]()
   157  	F[Int]()
   158  	F[GlobalInt]()
   159  
   160  	type U[_ any] int
   161  	type V U[int]
   162  	type W V
   163  
   164  	F[U[int]]()
   165  	F[U[Int]]()
   166  	F[U[GlobalInt]]()
   167  	F[V]()
   168  	F[W]()
   169  
   170  	type X[A any] U[X[A]]
   171  
   172  	F[X[int]]()
   173  	F[X[Int]]()
   174  	F[X[GlobalInt]]()
   175  
   176  	for j, tj := range tests {
   177  		for i, ti := range tests[:j+1] {
   178  			if (ti.TArgs == tj.TArgs) != (ti.Instance == tj.Instance) {
   179  				fmt.Printf("FAIL: %d,%d: %s, but %s\n", i, j, eq(ti.TArgs, tj.TArgs), eq(ti.Instance, tj.Instance))
   180  			}
   181  
   182  			// The test is constructed so we should see a few identical types.
   183  			// See "NOTE" comments above.
   184  			if i != j && ti.Instance == tj.Instance {
   185  				fmt.Printf("%d,%d: %v\n", i, j, ti.Instance)
   186  			}
   187  		}
   188  	}
   189  }
   190  
   191  func eq(a, b interface{}) string {
   192  	op := "=="
   193  	if a != b {
   194  		op = "!="
   195  	}
   196  	return fmt.Sprintf("%v %s %v", a, op, b)
   197  }
   198  `
   199  	out := `0,3: main.T[int;int]
   200  4,7: main.T[int;main.U[int;int]·3]
   201  22,23: main.T[main.Int;main.Int]
   202  26,27: main.T[main.Int;main.U[main.Int;main.Int]·3]
   203  `
   204  	ctx := igop.NewContext(0)
   205  	var buf bytes.Buffer
   206  	ctx.RegisterExternal("fmt.Printf", func(format string, a ...interface{}) (n int, err error) {
   207  		fmt.Fprintf(&buf, format, a...)
   208  		return fmt.Printf(format, a...)
   209  	})
   210  	_, err := ctx.RunFile("main.go", src, nil)
   211  	if err != nil {
   212  		t.Fatal(err)
   213  	}
   214  	if buf.String() != out {
   215  		t.Fatal("error output", buf.String())
   216  	}
   217  }
   218  
   219  func TestNestedTypeParams(t *testing.T) {
   220  	src := `package main
   221  
   222  func T[N ~int](v N) {
   223  	type T []N
   224  	var t T
   225  	println(t)
   226  }
   227  
   228  func main() {
   229  	T(100)
   230  }
   231  `
   232  	_, err := igop.RunFile("main.go", src, nil, 0)
   233  	if err != nil {
   234  		t.Fatal(err)
   235  	}
   236  }
   237  
   238  func TestTypeParamsRecursive(t *testing.T) {
   239  	src := `package main
   240  
   241  import "fmt"
   242  
   243  func main() {
   244  	recursive()
   245  }	
   246  
   247  func recursive() {
   248  	type T int
   249  	if got, want := recur1[T](5), T(110); got != want {
   250  		panic(fmt.Sprintf("recur1[T](5) = %d, want = %d", got, want))
   251  	}
   252  }
   253  
   254  type Integer interface {
   255  	~int | ~int32 | ~int64
   256  }
   257  
   258  func recur1[T Integer](n T) T {
   259  	if n == 0 || n == 1 {
   260  		return T(1)
   261  	} else {
   262  		return n * recur2(n-1)
   263  	}
   264  }
   265  
   266  func recur2[T Integer](n T) T {
   267  	list := make([]T, n)
   268  	for i, _ := range list {
   269  		list[i] = T(i + 1)
   270  	}
   271  	var sum T
   272  	for _, elt := range list {
   273  		sum += elt
   274  	}
   275  	return sum + recur1(n-1)
   276  }
   277  `
   278  	_, err := igop.RunFile("main.go", src, nil, 0)
   279  	if err != nil {
   280  		t.Fatal(err)
   281  	}
   282  }
   283  
   284  func TestAtomicPointer(t *testing.T) {
   285  	if runtime.Version()[:6] == "go1.18" {
   286  		t.Skip("skip go1.18")
   287  	}
   288  	src := `package main
   289  
   290  import (
   291  	"sync/atomic"
   292  )
   293  
   294  func main() {
   295  	var i atomic.Int64
   296  	i.Store(200)
   297  	if i.Load() != 200 {
   298  		panic("error atomic.Int64")
   299  	}
   300  	var v atomic.Pointer[int]
   301  	var n int = 200
   302  	v.Store(&n)
   303  	if p := v.Load(); *p != 200 {
   304  		panic("error atomic.Pointer[int]")
   305  	}
   306  	var v2 atomic.Pointer[string]
   307  	var n2 string = "hello"
   308  	v2.Store(&n2)
   309  	if p := v2.Load(); *p != "hello" {
   310  		panic("error atomic.Pointer[string]")
   311  	}
   312  }
   313  `
   314  	_, err := igop.RunFile("main.go", src, nil, 0)
   315  	if err != nil {
   316  		t.Fatal(err)
   317  	}
   318  }
   319  
   320  func TestAtomicTypeArgs(t *testing.T) {
   321  	// type FileSet struct {
   322  	// 	mutex sync.RWMutex         // protects the file set
   323  	// 	base  int                  // base offset for the next file
   324  	// 	files []*File              // list of files in the order added to the set
   325  	// 	last  atomic.Pointer[File] // cache of last file looked up
   326  	// }
   327  	src := `package main
   328  
   329  import (
   330  	"go/token"
   331  )
   332  
   333  type CFG struct {
   334  }
   335  
   336  func (g *CFG) Format(fset *token.FileSet) {
   337  }
   338  
   339  func main() {
   340  }`
   341  	_, err := igop.RunFile("main.go", src, nil, 0)
   342  	if err != nil {
   343  		t.Fatal(err)
   344  	}
   345  }