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 }