diff --git a/pkg/compiler/analysis.go b/pkg/compiler/analysis.go index 23ab6dede..b3ca6954a 100644 --- a/pkg/compiler/analysis.go +++ b/pkg/compiler/analysis.go @@ -397,6 +397,13 @@ func (c *codegen) analyzeFuncAndGlobalVarUsage() funcUsage { nodeCache[name] = declPair{n, c.importMap, pkgPath} return false // will be processed in the next stage case *ast.GenDecl: + // Filter out generics usage. + err := c.checkGenericsGenDecl(n, pkgPath) + if err != nil { + c.prog.Err = err + return false // Program is invalid. + } + // After skipping all funcDecls, we are sure that each value spec // is a globally declared variable or constant. We need to gather global // vars from both main and imported packages. @@ -575,6 +582,23 @@ func (c *codegen) checkGenericsFuncDecl(n *ast.FuncDecl, funcName string) error return nil } +// checkGenericsGenDecl checks whether provided ast.GenDecl has generic code. +func (c *codegen) checkGenericsGenDecl(n *ast.GenDecl, pkgPath string) error { + // Generic type declaration: + // type List[T any] struct + // type List[T any] interface + if n.Tok == token.TYPE { + for _, s := range n.Specs { + typeSpec := s.(*ast.TypeSpec) + if typeSpec.TypeParams != nil { + return fmt.Errorf("%w: type %s is generic", ErrGenericsUnsuppored, c.getIdentName(pkgPath, typeSpec.Name.Name)) + } + } + } + + return nil +} + // nodeContext contains ast node with the corresponding import map, type info and package information // required to retrieve fully qualified node name (if so). type nodeContext struct { diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 4ee702626..1a609f8fc 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -566,6 +566,13 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { // x = 2 // ) case *ast.GenDecl: + // Filter out generics usage. + err := c.checkGenericsGenDecl(n, c.currPkg.PkgPath) + if err != nil { + c.prog.Err = err + return nil // Program is invalid. + } + if n.Tok == token.VAR || n.Tok == token.CONST { c.saveSequencePoint(n) } diff --git a/pkg/compiler/generics_test.go b/pkg/compiler/generics_test.go index 28eb00fda..0bf58aeec 100644 --- a/pkg/compiler/generics_test.go +++ b/pkg/compiler/generics_test.go @@ -51,3 +51,41 @@ func TestGenericFuncArgument(t *testing.T) { _, _, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil) require.ErrorIs(t, err, compiler.ErrGenericsUnsuppored) } + +func TestGenericTypeDecl(t *testing.T) { + t.Run("global scope", func(t *testing.T) { + src := ` + package sum + type List[T any] struct { + next *List[T] + val T + } + + func Main() any { + l := List[int]{} + return l + } +` + _, _, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil) + require.ErrorIs(t, err, compiler.ErrGenericsUnsuppored) + }) + t.Run("local scope", func(t *testing.T) { + src := ` + package sum + func Main() any { + type ( + SomeGoodType int + + List[T any] struct { + next *List[T] + val T + } + ) + l := List[int]{} + return l + } +` + _, _, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil) + require.ErrorIs(t, err, compiler.ErrGenericsUnsuppored) + }) +}