From c442ac50dbe91802e532246c1c0f646effbe9b48 Mon Sep 17 00:00:00 2001 From: Nevkontakte Date: Sun, 16 Oct 2022 14:48:34 +0100 Subject: [PATCH 1/3] Ignore another variant of the flag for enabling generics. --- tests/gorepo/run.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gorepo/run.go b/tests/gorepo/run.go index 3308ea5df..1ead4216e 100644 --- a/tests/gorepo/run.go +++ b/tests/gorepo/run.go @@ -700,7 +700,7 @@ func (t *test) run() { supportedArgs := []string{} for _, a := range args { switch a { - case "-gcflags=-G=3": + case "-gcflags=-G=3", `-gcflags="-G=3"`: continue default: supportedArgs = append(supportedArgs, a) From 8b180b42e435abb95d0713175203101c020c2eca Mon Sep 17 00:00:00 2001 From: Nevkontakte Date: Sun, 16 Oct 2022 15:15:13 +0100 Subject: [PATCH 2/3] Fix a bug in variable level detection. Previously it would incorrectly return `varGenericFactory` for local variables which type was exactly the type parameter, instead of `varFuncLocal`. This was causing local variables not to get declared in the list of local variables. This change brings typeparams tests to 33 pass / 98 fail. --- compiler/package.go | 2 +- compiler/utils.go | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/compiler/package.go b/compiler/package.go index 7838e8eb3..af72e9c86 100644 --- a/compiler/package.go +++ b/compiler/package.go @@ -531,7 +531,7 @@ func Compile(importPath string, files []*ast.File, fileSet *token.FileSet, impor d.DeclCode = funcCtx.CatchOutput(0, func() { typeName := funcCtx.objectName(o) lhs := typeName - if typeVarLevel(o) == varPackage { + if getVarLevel(o) == varPackage { lhs += " = $pkg." + encodeIdent(o.Name()) } size := int64(0) diff --git a/compiler/utils.go b/compiler/utils.go index 51cd85773..fd724b0f8 100644 --- a/compiler/utils.go +++ b/compiler/utils.go @@ -368,8 +368,17 @@ func isVarOrConst(o types.Object) bool { return false } -func typeVarLevel(o types.Object) varLevel { - if _, ok := o.Type().(*types.TypeParam); ok { +func isTypeParameterName(o types.Object) bool { + _, isTypeName := o.(*types.TypeName) + _, isTypeParam := o.Type().(*types.TypeParam) + return isTypeName && isTypeParam +} + +// getVarLevel returns at which level a JavaScript variable for the given object +// should be defined. The object can represent any named Go object: variable, +// type, function, etc. +func getVarLevel(o types.Object) varLevel { + if isTypeParameterName(o) { return varGenericFactory } if o.Parent() != nil && o.Parent().Parent() == types.Universe { @@ -381,7 +390,7 @@ func typeVarLevel(o types.Object) varLevel { // objectName returns a JS identifier corresponding to the given types.Object. // Repeated calls for the same object will return the same name. func (fc *funcContext) objectName(o types.Object) string { - if typeVarLevel(o) == varPackage { + if getVarLevel(o) == varPackage { fc.pkgCtx.dependencies[o] = true if o.Pkg() != fc.pkgCtx.Pkg || (isVarOrConst(o) && o.Exported()) { @@ -391,7 +400,7 @@ func (fc *funcContext) objectName(o types.Object) string { name, ok := fc.pkgCtx.objectNames[o] if !ok { - name = fc.newVariable(o.Name(), typeVarLevel(o)) + name = fc.newVariable(o.Name(), getVarLevel(o)) fc.pkgCtx.objectNames[o] = name } @@ -402,13 +411,13 @@ func (fc *funcContext) objectName(o types.Object) string { } func (fc *funcContext) varPtrName(o *types.Var) string { - if typeVarLevel(o) == varPackage && o.Exported() { + if getVarLevel(o) == varPackage && o.Exported() { return fc.pkgVar(o.Pkg()) + "." + o.Name() + "$ptr" } name, ok := fc.pkgCtx.varPtrNames[o] if !ok { - name = fc.newVariable(o.Name()+"$ptr", typeVarLevel(o)) + name = fc.newVariable(o.Name()+"$ptr", getVarLevel(o)) fc.pkgCtx.varPtrNames[o] = name } return name From f34879335dabe9696a633a0b2f36fe601f5c8018 Mon Sep 17 00:00:00 2001 From: Nevkontakte Date: Sun, 16 Oct 2022 16:11:39 +0100 Subject: [PATCH 3/3] Fix zero value initialization for generic-typed variables. Since we don't know the concrete type at compile time, we use a runtime call `TypeName.zero()` to get the zero value. In the compiler side this works in a bit of a roundabout way: - Statements like `var x int` are converted to `x := 0`, where zero value is determined by the compiler based on the variable's underlying type. - For a generic type `T` the underlying type is interface, so the compiler rewrites `var x T` as `x := nil // x has type T`. This is not entirely correct from the language specification perspective, because there is no universal zero value literal for a generic type T. However, this is the closest approximation we can express as an AST. - When compiling a nil literal into a generic-typed expression we assume this was meant to represent the zero value and invoke the runtime `T.zero()` method for the generic type T. --- compiler/expressions.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/expressions.go b/compiler/expressions.go index 1d86174ce..fa71a4912 100644 --- a/compiler/expressions.go +++ b/compiler/expressions.go @@ -768,6 +768,9 @@ func (fc *funcContext) translateExpr(expr ast.Expr) *expression { if typesutil.IsJsObject(exprType) { return fc.formatExpr("null") } + if typesutil.IsGeneric(exprType) { + return fc.formatExpr("%s.zero()", fc.typeName(exprType)) + } switch t := exprType.Underlying().(type) { case *types.Basic: if t.Kind() != types.UnsafePointer {