Skip to content

Commit 6de32f8

Browse files
authored
Merge pull request #1272 from nevkontakte/generics-ng
An alternate approach to generics support
2 parents e76f823 + 2ce9bec commit 6de32f8

32 files changed

+2957
-448
lines changed

compiler/analysis/info.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/gopherjs/gopherjs/compiler/astutil"
1111
"github.com/gopherjs/gopherjs/compiler/typesutil"
12+
"golang.org/x/exp/typeparams"
1213
)
1314

1415
type continueStmt struct {
@@ -342,8 +343,8 @@ func (fi *FuncInfo) visitCallExpr(n *ast.CallExpr) ast.Visitor {
342343
return nil // No need to walk under this CallExpr, we already did it manually.
343344
default:
344345
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.
346+
// This is a type conversion, not a call. Type assertion itself is not
347+
// blocking, but we will visit the input expression.
347348
} else {
348349
// The function is returned by a non-trivial expression. We have to be
349350
// conservative and assume that function might be blocking.
@@ -357,6 +358,7 @@ func (fi *FuncInfo) visitCallExpr(n *ast.CallExpr) ast.Visitor {
357358
func (fi *FuncInfo) callToNamedFunc(callee types.Object) {
358359
switch o := callee.(type) {
359360
case *types.Func:
361+
o = typeparams.OriginMethod(o) // TODO(nevkontakte): Can be replaced with o.Origin() in Go 1.19.
360362
if recv := o.Type().(*types.Signature).Recv(); recv != nil {
361363
if _, ok := recv.Type().Underlying().(*types.Interface); ok {
362364
// Conservatively assume that an interface implementation may be blocking.

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/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)