Skip to content

Commit c0be063

Browse files
authored
Merge pull request #1290 from gopherjs/generics-ng
Foundations of generics support
2 parents 42009d4 + 852c4d2 commit c0be063

40 files changed

+3574
-495
lines changed

build/build_test.go

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -418,18 +418,17 @@ func TestOverlayAugmentation(t *testing.T) {
418418
test.want = test.src
419419
}
420420

421-
fsetSrc := token.NewFileSet()
422-
fileSrc := srctesting.Parse(t, fsetSrc, pkgName+test.src)
421+
f := srctesting.New(t)
422+
fileSrc := f.Parse("test.go", pkgName+test.src)
423423

424424
overrides := map[string]overrideInfo{}
425425
augmentOverlayFile(fileSrc, overrides)
426426
pruneImports(fileSrc)
427427

428-
got := srctesting.Format(t, fsetSrc, fileSrc)
428+
got := srctesting.Format(t, f.FileSet, fileSrc)
429429

430-
fsetWant := token.NewFileSet()
431-
fileWant := srctesting.Parse(t, fsetWant, pkgName+test.want)
432-
want := srctesting.Format(t, fsetWant, fileWant)
430+
fileWant := f.Parse("test.go", pkgName+test.want)
431+
want := srctesting.Format(t, f.FileSet, fileWant)
433432

434433
if got != want {
435434
t.Errorf("augmentOverlayFile and pruneImports got unexpected code:\n"+
@@ -720,18 +719,17 @@ func TestOriginalAugmentation(t *testing.T) {
720719
t.Run(test.desc, func(t *testing.T) {
721720
pkgName := "package testpackage\n\n"
722721
importPath := `math/rand`
723-
fsetSrc := token.NewFileSet()
724-
fileSrc := srctesting.Parse(t, fsetSrc, pkgName+test.src)
722+
f := srctesting.New(t)
723+
fileSrc := f.Parse("test.go", pkgName+test.src)
725724

726725
augmentOriginalImports(importPath, fileSrc)
727726
augmentOriginalFile(fileSrc, test.info)
728727
pruneImports(fileSrc)
729728

730-
got := srctesting.Format(t, fsetSrc, fileSrc)
729+
got := srctesting.Format(t, f.FileSet, fileSrc)
731730

732-
fsetWant := token.NewFileSet()
733-
fileWant := srctesting.Parse(t, fsetWant, pkgName+test.want)
734-
want := srctesting.Format(t, fsetWant, fileWant)
731+
fileWant := f.Parse("test.go", pkgName+test.want)
732+
want := srctesting.Format(t, f.FileSet, fileWant)
735733

736734
if got != want {
737735
t.Errorf("augmentOriginalImports, augmentOriginalFile, and pruneImports got unexpected code:\n"+

circle.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ orbs:
7474
jobs:
7575
build:
7676
executor: gopherjs
77+
environment:
78+
GOPHERJS_EXPERIMENT: generics
7779
steps:
7880
- setup_and_install_gopherjs
7981
- run:
@@ -129,6 +131,8 @@ jobs:
129131
gopherjs_tests:
130132
executor: gopherjs
131133
parallelism: 4
134+
environment:
135+
GOPHERJS_EXPERIMENT: generics
132136
steps:
133137
- setup_and_install_gopherjs
134138
- run:
@@ -155,6 +159,8 @@ jobs:
155159

156160
gorepo_tests:
157161
executor: gopherjs
162+
environment:
163+
GOPHERJS_EXPERIMENT: generics
158164
parallelism: 4
159165
steps:
160166
- setup_environment
@@ -170,6 +176,8 @@ jobs:
170176
executor:
171177
name: win/default
172178
shell: powershell.exe
179+
environment:
180+
GOPHERJS_EXPERIMENT: generics
173181
steps:
174182
- checkout
175183
- run:
@@ -204,6 +212,8 @@ jobs:
204212
darwin_smoke:
205213
macos:
206214
xcode: 13.4.1 # Mac OS 12.6.1, see https://circleci.com/docs/using-macos/
215+
environment:
216+
GOPHERJS_EXPERIMENT: generics
207217
steps:
208218
- checkout
209219
- setup_environment

compiler/analysis/info.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,8 @@ func (fi *FuncInfo) visitCallExpr(n *ast.CallExpr) ast.Visitor {
342342
return nil // No need to walk under this CallExpr, we already did it manually.
343343
default:
344344
if astutil.IsTypeExpr(f, fi.pkgInfo.Info) {
345-
// This is a type assertion, not a call. Type assertion itself is not
346-
// blocking, but we will visit the expression itself.
345+
// This is a type conversion, not a call. Type assertion itself is not
346+
// blocking, but we will visit the input expression.
347347
} else {
348348
// The function is returned by a non-trivial expression. We have to be
349349
// conservative and assume that function might be blocking.
@@ -357,6 +357,7 @@ func (fi *FuncInfo) visitCallExpr(n *ast.CallExpr) ast.Visitor {
357357
func (fi *FuncInfo) callToNamedFunc(callee types.Object) {
358358
switch o := callee.(type) {
359359
case *types.Func:
360+
o = o.Origin()
360361
if recv := o.Type().(*types.Signature).Recv(); recv != nil {
361362
if _, ok := recv.Type().Underlying().(*types.Interface); ok {
362363
// Conservatively assume that an interface implementation may be blocking.

compiler/analysis/info_test.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package analysis
22

33
import (
44
"go/ast"
5-
"go/token"
65
"go/types"
76
"testing"
87

@@ -34,11 +33,11 @@ func notBlocking() {
3433
func() { println() } ()
3534
}
3635
`
37-
fset := token.NewFileSet()
38-
file := srctesting.Parse(t, fset, src)
39-
typesInfo, typesPkg := srctesting.Check(t, fset, file)
36+
f := srctesting.New(t)
37+
file := f.Parse("test.go", src)
38+
typesInfo, typesPkg := f.Check("pkg/test", file)
4039

41-
pkgInfo := AnalyzePkg([]*ast.File{file}, fset, typesInfo, typesPkg, func(f *types.Func) bool {
40+
pkgInfo := AnalyzePkg([]*ast.File{file}, f.FileSet, typesInfo, typesPkg, func(f *types.Func) bool {
4241
panic("isBlocking() should be never called for imported functions in this test.")
4342
})
4443

compiler/astutil/astutil.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,15 @@ func NewIdent(name string, t types.Type, info *types.Info, pkg *types.Package) *
3535
return ident
3636
}
3737

38+
// IsTypeExpr returns true if expr denotes a type. This can be used to
39+
// distinguish between calls and type conversions.
3840
func IsTypeExpr(expr ast.Expr, info *types.Info) bool {
41+
// Note that we could've used info.Types[expr].IsType() instead of doing our
42+
// own analysis. However, that creates a problem because we synthesize some
43+
// *ast.CallExpr nodes and, more importantly, *ast.Ident nodes that denote a
44+
// type. Unfortunately, because the flag that controls
45+
// types.TypeAndValue.IsType() return value is unexported we wouldn't be able
46+
// to set it correctly. Thus, we can't rely on IsType().
3947
switch e := expr.(type) {
4048
case *ast.ArrayType, *ast.ChanType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.StructType:
4149
return true
@@ -47,6 +55,20 @@ func IsTypeExpr(expr ast.Expr, info *types.Info) bool {
4755
case *ast.SelectorExpr:
4856
_, ok := info.Uses[e.Sel].(*types.TypeName)
4957
return ok
58+
case *ast.IndexExpr:
59+
ident, ok := e.X.(*ast.Ident)
60+
if !ok {
61+
return false
62+
}
63+
_, ok = info.Uses[ident].(*types.TypeName)
64+
return ok
65+
case *ast.IndexListExpr:
66+
ident, ok := e.X.(*ast.Ident)
67+
if !ok {
68+
return false
69+
}
70+
_, ok = info.Uses[ident].(*types.TypeName)
71+
return ok
5072
case *ast.ParenExpr:
5173
return IsTypeExpr(e.X, info)
5274
default:

compiler/astutil/astutil_test.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package astutil
22

33
import (
44
"go/ast"
5-
"go/token"
65
"strconv"
76
"testing"
87

@@ -44,8 +43,7 @@ func TestImportsUnsafe(t *testing.T) {
4443
for _, test := range tests {
4544
t.Run(test.desc, func(t *testing.T) {
4645
src := "package testpackage\n\n" + test.imports
47-
fset := token.NewFileSet()
48-
file := srctesting.Parse(t, fset, src)
46+
file := srctesting.New(t).Parse("test.go", src)
4947
got := ImportsUnsafe(file)
5048
if got != test.want {
5149
t.Fatalf("ImportsUnsafe() returned %t, want %t", got, test.want)
@@ -81,8 +79,7 @@ func TestImportName(t *testing.T) {
8179
for _, test := range tests {
8280
t.Run(test.desc, func(t *testing.T) {
8381
src := "package testpackage\n\n" + test.src
84-
fset := token.NewFileSet()
85-
file := srctesting.Parse(t, fset, src)
82+
file := srctesting.New(t).Parse("test.go", src)
8683
if len(file.Imports) != 1 {
8784
t.Fatal(`expected one and only one import`)
8885
}
@@ -399,8 +396,7 @@ func TestHasDirectiveOnFile(t *testing.T) {
399396
for _, test := range tests {
400397
t.Run(test.desc, func(t *testing.T) {
401398
const action = `do-stuff`
402-
fset := token.NewFileSet()
403-
file := srctesting.Parse(t, fset, test.src)
399+
file := srctesting.New(t).Parse("test.go", test.src)
404400
if got := hasDirective(file, action); got != test.want {
405401
t.Errorf(`hasDirective(%T, %q) returned %t, want %t`, file, action, got, test.want)
406402
}

compiler/compiler.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"strings"
1818
"time"
1919

20+
"github.com/gopherjs/gopherjs/compiler/internal/symbol"
2021
"github.com/gopherjs/gopherjs/compiler/prelude"
2122
"golang.org/x/tools/go/gcexportdata"
2223
)
@@ -112,9 +113,13 @@ type Decl struct {
112113
// Go compiler/linker toolchain. Used by GopherJS to support go:linkname
113114
// directives. Must be set for decls that are supported by go:linkname
114115
// implementation.
115-
LinkingName SymName
116+
LinkingName symbol.Name
116117
// A list of package-level JavaScript variable names this symbol needs to declare.
117118
Vars []string
119+
// A JS expression by which the object represented by this decl may be
120+
// referenced within the package context. Empty if the decl represents no such
121+
// object.
122+
RefExpr string
118123
// NamedRecvType is method named recv declare.
119124
NamedRecvType string
120125
// JavaScript code that declares basic information about a symbol. For a type
@@ -326,7 +331,7 @@ func WritePkgCode(pkg *Archive, dceSelection map[*Decl]struct{}, gls goLinknameS
326331
if recv, method, ok := d.LinkingName.IsMethod(); ok {
327332
code = fmt.Sprintf("\t$linknames[%q] = $unsafeMethodToFunction(%v,%q,%t);\n", d.LinkingName.String(), d.NamedRecvType, method, strings.HasPrefix(recv, "*"))
328333
} else {
329-
code = fmt.Sprintf("\t$linknames[%q] = %s;\n", d.LinkingName.String(), d.Vars[0])
334+
code = fmt.Sprintf("\t$linknames[%q] = %s;\n", d.LinkingName.String(), d.RefExpr)
330335
}
331336
if _, err := w.Write(removeWhitespace([]byte(code), minify)); err != nil {
332337
return err
@@ -357,7 +362,7 @@ func WritePkgCode(pkg *Archive, dceSelection map[*Decl]struct{}, gls goLinknameS
357362
if !found {
358363
continue // The symbol is not affected by a go:linkname directive.
359364
}
360-
lines = append(lines, fmt.Sprintf("\t\t%s = $linknames[%q];\n", d.Vars[0], impl.String()))
365+
lines = append(lines, fmt.Sprintf("\t\t%s = $linknames[%q];\n", d.RefExpr, impl.String()))
361366
}
362367
if len(lines) > 0 {
363368
code := fmt.Sprintf("\t$pkg.$initLinknames = function() {\n%s};\n", strings.Join(lines, ""))

0 commit comments

Comments
 (0)