@@ -16,12 +16,14 @@ import (
16
16
"go/ast"
17
17
"go/build"
18
18
"go/constant"
19
+ "go/importer"
19
20
"go/parser"
20
21
"go/token"
21
22
"go/types"
22
23
"io"
23
24
"math/big"
24
25
"os"
26
+ "path/filepath"
25
27
"reflect"
26
28
"runtime"
27
29
"sort"
@@ -454,3 +456,148 @@ func TestUnexportedStructFields(t *testing.T) {
454
456
type importerFunc func (path string ) (* types.Package , error )
455
457
456
458
func (f importerFunc ) Import (path string ) (* types.Package , error ) { return f (path ) }
459
+
460
+ // TestIExportDataTypeParameterizedAliases tests IExportData
461
+ // on both declarations and uses of type parameterized aliases.
462
+ func TestIExportDataTypeParameterizedAliases (t * testing.T ) {
463
+ testenv .NeedsGo1Point (t , 23 )
464
+
465
+ testenv .NeedsGoExperiment (t , "aliastypeparams" )
466
+ t .Setenv ("GODEBUG" , "gotypesalias=1" )
467
+
468
+ // High level steps:
469
+ // * parse and typecheck
470
+ // * export the data for the importer (via IExportData),
471
+ // * import the data (via either x/tools or GOROOT's gcimporter), and
472
+ // * check the imported types.
473
+
474
+ const src = `package a
475
+
476
+ type A[T any] = *T
477
+ type B[R any, S *R] = []S
478
+ type C[U any] = B[U, A[U]]
479
+
480
+ type Named int
481
+ type Chained = C[Named] // B[Named, A[Named]] = B[Named, *Named] = []*Named
482
+ `
483
+
484
+ // parse and typecheck
485
+ fset1 := token .NewFileSet ()
486
+ f , err := parser .ParseFile (fset1 , "a" , src , 0 )
487
+ if err != nil {
488
+ t .Fatal (err )
489
+ }
490
+ var conf types.Config
491
+ pkg1 , err := conf .Check ("a" , fset1 , []* ast.File {f }, nil )
492
+ if err != nil {
493
+ t .Fatal (err )
494
+ }
495
+
496
+ testcases := map [string ]func (t * testing.T ) * types.Package {
497
+ // Read the result of IExportData through x/tools/internal/gcimporter.IImportData.
498
+ "tools" : func (t * testing.T ) * types.Package {
499
+ // export
500
+ exportdata , err := iexport (fset1 , gcimporter .IExportVersion , pkg1 )
501
+ if err != nil {
502
+ t .Fatal (err )
503
+ }
504
+
505
+ // import
506
+ imports := make (map [string ]* types.Package )
507
+ fset2 := token .NewFileSet ()
508
+ _ , pkg2 , err := gcimporter .IImportData (fset2 , imports , exportdata , pkg1 .Path ())
509
+ if err != nil {
510
+ t .Fatalf ("IImportData(%s): %v" , pkg1 .Path (), err )
511
+ }
512
+ return pkg2
513
+ },
514
+ // Read the result of IExportData through $GOROOT/src/internal/gcimporter.IImportData.
515
+ //
516
+ // This test fakes creating an old go object file in indexed format.
517
+ // This means that it can be loaded by go/importer or go/types.
518
+ // This step is not supported, but it does give test coverage for stdlib.
519
+ "goroot" : func (t * testing.T ) * types.Package {
520
+ t .Skip ("Fix bug in src/internal/gcimporter.IImportData for aliasType then reenable" )
521
+
522
+ // Write indexed export data file contents.
523
+ //
524
+ // TODO(taking): Slightly unclear to what extent this step should be supported by go/importer.
525
+ var buf bytes.Buffer
526
+ buf .WriteString ("go object \n $$B\n " ) // object file header
527
+ if err := gcexportdata .Write (& buf , fset1 , pkg1 ); err != nil {
528
+ t .Fatal (err )
529
+ }
530
+
531
+ // Write export data to temporary file
532
+ out := t .TempDir ()
533
+ name := filepath .Join (out , "a.out" )
534
+ if err := os .WriteFile (name + ".a" , buf .Bytes (), 0644 ); err != nil {
535
+ t .Fatal (err )
536
+ }
537
+ pkg2 , err := importer .Default ().Import (name )
538
+ if err != nil {
539
+ t .Fatal (err )
540
+ }
541
+ return pkg2
542
+ },
543
+ }
544
+
545
+ for name , importer := range testcases {
546
+ t .Run (name , func (t * testing.T ) {
547
+ pkg := importer (t )
548
+
549
+ obj := pkg .Scope ().Lookup ("A" )
550
+ if obj == nil {
551
+ t .Fatalf ("failed to find %q in package %s" , "A" , pkg )
552
+ }
553
+
554
+ // Check that A is type A[T any] = *T.
555
+ // TODO(taking): fix how go/types prints parameterized aliases to simplify tests.
556
+ alias , ok := obj .Type ().(* aliases.Alias )
557
+ if ! ok {
558
+ t .Fatalf ("Obj %s is not an Alias" , obj )
559
+ }
560
+
561
+ targs := aliases .TypeArgs (alias )
562
+ if targs .Len () != 0 {
563
+ t .Errorf ("%s has %d type arguments. expected 0" , alias , targs .Len ())
564
+ }
565
+
566
+ tparams := aliases .TypeParams (alias )
567
+ if tparams .Len () != 1 {
568
+ t .Fatalf ("%s has %d type arguments. expected 1" , alias , targs .Len ())
569
+ }
570
+ tparam := tparams .At (0 )
571
+ if got , want := tparam .String (), "T" ; got != want {
572
+ t .Errorf ("(%q).TypeParams().At(0)=%q. want %q" , alias , got , want )
573
+ }
574
+
575
+ anyt := types .Universe .Lookup ("any" ).Type ()
576
+ if c := tparam .Constraint (); ! types .Identical (anyt , c ) {
577
+ t .Errorf ("(%q).Constraint()=%q. expected %q" , tparam , c , anyt )
578
+ }
579
+
580
+ ptparam := types .NewPointer (tparam )
581
+ if rhs := aliases .Rhs (alias ); ! types .Identical (ptparam , rhs ) {
582
+ t .Errorf ("(%q).Rhs()=%q. expected %q" , alias , rhs , ptparam )
583
+ }
584
+
585
+ // TODO(taking): add tests for B and C once it is simpler to write tests.
586
+
587
+ chained := pkg .Scope ().Lookup ("Chained" )
588
+ if chained == nil {
589
+ t .Fatalf ("failed to find %q in package %s" , "Chained" , pkg )
590
+ }
591
+
592
+ named , _ := pkg .Scope ().Lookup ("Named" ).(* types.TypeName )
593
+ if named == nil {
594
+ t .Fatalf ("failed to find %q in package %s" , "Named" , pkg )
595
+ }
596
+
597
+ want := types .NewSlice (types .NewPointer (named .Type ()))
598
+ if got := chained .Type (); ! types .Identical (got , want ) {
599
+ t .Errorf ("(%q).Type()=%q which should be identical to %q" , chained , got , want )
600
+ }
601
+ })
602
+ }
603
+ }
0 commit comments