Skip to content

Commit 1b1b454

Browse files
committedAug 18, 2023
compiler: disallow generic method receiver
Either non-pointer or pointer, both cases are disallowed to be generic. Need to be reverted and properly handled within the scope of #2376. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
1 parent 415d447 commit 1b1b454

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed
 

‎pkg/compiler/analysis.go

+35
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ var (
1919
ErrMissingExportedParamName = errors.New("exported method is not allowed to have unnamed parameter")
2020
// ErrInvalidExportedRetCount is returned when exported contract method has invalid return values count.
2121
ErrInvalidExportedRetCount = errors.New("exported method is not allowed to have more than one return value")
22+
// ErrGenericsUnsuppored is returned when generics-related tokens are encountered.
23+
ErrGenericsUnsuppored = errors.New("generics are currently unsupported, please, see the https://github.com/nspcc-dev/neo-go/issues/2376")
2224
)
2325

2426
var (
@@ -361,6 +363,13 @@ func (c *codegen) analyzeFuncAndGlobalVarUsage() funcUsage {
361363
case *ast.FuncDecl:
362364
name := c.getFuncNameFromDecl(pkgPath, n)
363365

366+
// filter out generic functions
367+
err := c.checkGenericsFuncDecl(n, name)
368+
if err != nil {
369+
c.prog.Err = err
370+
return false // Program is invalid.
371+
}
372+
364373
// exported functions and methods are always assumed to be used
365374
if isMain && n.Name.IsExported() || isInitFunc(n) || isDeployFunc(n) {
366375
diff[name] = true
@@ -535,6 +544,32 @@ func (c *codegen) analyzeFuncAndGlobalVarUsage() funcUsage {
535544
return usage
536545
}
537546

547+
// checkGenericFuncDecl checks whether provided ast.FuncDecl has generic code.
548+
func (c *codegen) checkGenericsFuncDecl(n *ast.FuncDecl, funcName string) error {
549+
var errGenerics error
550+
551+
// Generic function receiver.
552+
if n.Recv != nil {
553+
switch t := n.Recv.List[0].Type.(type) {
554+
case *ast.StarExpr:
555+
switch t.X.(type) {
556+
case *ast.IndexExpr:
557+
// func (x *Pointer[T]) Load() *T
558+
errGenerics = errors.New("generic pointer function receiver")
559+
}
560+
case *ast.IndexExpr:
561+
// func (x Structure[T]) Load() *T
562+
errGenerics = errors.New("generic function receiver")
563+
}
564+
}
565+
566+
if errGenerics != nil {
567+
return fmt.Errorf("%w: %s has %s", ErrGenericsUnsuppored, funcName, errGenerics.Error())
568+
}
569+
570+
return nil
571+
}
572+
538573
// nodeContext contains ast node with the corresponding import map, type info and package information
539574
// required to retrieve fully qualified node name (if so).
540575
type nodeContext struct {

‎pkg/compiler/generics_test.go

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package compiler_test
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/nspcc-dev/neo-go/pkg/compiler"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestGenericMethodReceiver(t *testing.T) {
12+
t.Run("star expression", func(t *testing.T) {
13+
src := `
14+
package receiver
15+
type Pointer[T any] struct {
16+
value T
17+
}
18+
func (x *Pointer[T]) Load() *T {
19+
return &x.value
20+
}
21+
`
22+
_, _, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
23+
require.ErrorIs(t, err, compiler.ErrGenericsUnsuppored)
24+
})
25+
t.Run("ident expression", func(t *testing.T) {
26+
src := `
27+
package receiver
28+
type Pointer[T any] struct {
29+
value T
30+
}
31+
func (x Pointer[T]) Load() *T {
32+
return &x.value
33+
}
34+
`
35+
_, _, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
36+
require.ErrorIs(t, err, compiler.ErrGenericsUnsuppored)
37+
})
38+
}

0 commit comments

Comments
 (0)