Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions compiler/lowering.go
Original file line number Diff line number Diff line change
Expand Up @@ -10331,6 +10331,11 @@ func (o *LoweringOwner) lowerExpr(ctx lowerFileContext, expr ast.Expr) (string,
case *ast.TypeAssertExpr:
return o.lowerTypeAssertExpr(ctx, typed)
case *ast.IndexListExpr:
// A generic function instantiated with several type arguments, used
// as a value, keeps them, as lowerIndexExpr does for one.
if signature := genericFunctionSignature(ctx, typed.X); signature != nil {
return o.lowerGenericFunctionValue(ctx, typed.X, typed.Indices, signature)
}
return o.lowerExpr(ctx, typed.X)
default:
return "undefined", []Diagnostic{loweringUnsupportedAt(ctx, typed, "expression", ctx.semPkg.pkgPath, "unsupported expression kind")}
Expand Down Expand Up @@ -14705,7 +14710,14 @@ func (o *LoweringOwner) lowerGenericFunctionValue(
typeArgExprs []ast.Expr,
signature *types.Signature,
) (string, []Diagnostic) {
// An override function that takes no type arguments, such as those of
// the slices override, is its own value, as calls of it pass none.
calleeExpr, diagnostics := o.lowerExpr(ctx, callee)
if o.callUsesOverridePackage(ctx, callee) && !o.overrideFacts().TakesTypeArgs(calledFunction(ctx.semPkg.source, callee)) {
return calleeExpr, diagnostics
}

// Otherwise the value calls the function with the type arguments.
typeArgs := o.genericTypeArgsExpr(ctx, callee, typeArgExprs)
signatureCtx := ctx.withFunctionTypeDepth(ctx.functionTypeDepth + 1)
params := o.tsSignatureParamsFor(signatureCtx, signature, false)
Expand Down
5 changes: 5 additions & 0 deletions tests/tests/generic_func_value_type_args/expected.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
call neuron true
variable neuron true
map neuron true
single x
slices 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"slices"

"github.com/s4wave/goscript/tests/tests/generic_func_value_type_args/lib"
)

type Sim struct{}

type Config struct {
lib.BaseConfig
Rate float32
}

func (cfg *Config) Defaults() { cfg.Name = "neuron" }

// embed needs both of its type arguments.
func embed[S, C any](label string) {
_, cfg := lib.NewConfig[C]()
var s S
println(label, cfg.AsBaseConfig().Name, any(&s) != nil)
}

func first[T any](v T) T { return v }

func main() {
// A generic function instantiated with several type arguments keeps them
// when it is used as a value, as in a call.
embed[Sim, Config]("call")
f := embed[Sim, Config]
f("variable")
sims := map[string]func(string){"neuron": embed[Sim, Config]}
sims["neuron"]("map")

// So does one with a single type argument.
g := first[string]
println("single", g("x"))

// A function of the slices override, which takes no type arguments.
index := slices.Index[[]int, int]
println("slices", index([]int{4, 5, 6}, 5))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Generated file based on generic_func_value_type_args.go
// Updated when compliance tests are re-run, DO NOT EDIT!

import * as $ from "@goscript/builtin/index.js"
import * as slices from "@goscript/slices/index.js"
import * as lib from "@goscript/github.com/s4wave/goscript/tests/tests/generic_func_value_type_args/lib/index.js"
import "@goscript/slices/index.js"

export class Sim {
constructor(__goscriptValue?: boolean) {
;(this as any)[$.structValueMarker] = __goscriptValue === true
}

public clone(): Sim {
return new Sim(true)
}

public __goscriptAssign(source: Sim): this {
return this
}

static __typeInfo = $.registerStructType(
"main.Sim",
() => new Sim(),
() => [],
Sim,
() => []
)
}

export class Config {
public declare BaseConfig: lib.BaseConfig

public declare Rate: number

constructor(__goscriptValue?: boolean, BaseConfig$?: lib.BaseConfig, Rate$?: number) {
this.BaseConfig = BaseConfig$ ?? new lib.BaseConfig(true)
this.Rate = Rate$ ?? 0
;(this as any)[$.structValueMarker] = __goscriptValue === true
}

public clone(): Config {
return new Config(true, this.BaseConfig.clone(), this.Rate)
}

public __goscriptAssign(source: Config): this {
this.BaseConfig.__goscriptAssign(source.BaseConfig)
this.Rate = source.Rate
return this
}

public Defaults(): void {
const cfg: Config | $.VarRef<Config> | null = this;
$.pointerValue<Config>(cfg).BaseConfig.Name = "neuron"
}

public AsBaseConfig(): any {
return lib.BaseConfig.prototype.AsBaseConfig.call($.fieldRef(this, "BaseConfig"))
}

static __typeInfo = $.registerStructType(
"main.Config",
() => new Config(),
() => [{ name: "Defaults", args: [], returns: [], pointerReceiver: true }, { name: "AsBaseConfig", args: [], returns: [{ type: /* @__PURE__ */ $.pointerType("lib.BaseConfig") }], pointerReceiver: true }],
Config,
() => [{ name: "BaseConfig", key: "BaseConfig", type: "lib.BaseConfig", anonymous: true }, { name: "Rate", key: "Rate", type: /* @__PURE__ */ $.basicType("float32") }]
)
}

export async function embed(__typeArgs: $.GenericTypeArgs | undefined, label: string): globalThis.Promise<void> {
let [, cfg] = lib.NewConfig({[$.genericTypeArgsMarker]: $.genericTypeArgsBrand, C: __typeArgs?.["C"] ?? { type: { kind: $.TypeKind.Interface, methods: [] }, zero: () => null }})
let s: $.VarRef<any> = $.varRef($.genericZero(__typeArgs, "S", null))
await $.println(label, $.pointerValue<lib.BaseConfig>(((__goscriptAwait = (cfg !== null && (cfg as any).__isVarRef !== true && ((cfg as any).__goValue === undefined || (cfg as any).__goType?.charCodeAt(0) !== 42) ? (cfg as Exclude<lib.Config, null>) : $.pointerValue<Exclude<lib.Config, null>>(cfg)).AsBaseConfig(), __goscriptAwait instanceof Promise ? await __goscriptAwait : __goscriptAwait) as lib.BaseConfig | $.VarRef<lib.BaseConfig> | null)).Name, $.typeParamPointerInterfaceValue(s, __typeArgs?.["S"], "*S", /* @__PURE__ */ $.pointerType(__typeArgs?.["S"]?.type ?? { kind: $.TypeKind.Interface, methods: [] })) != null)
}

export function first(__typeArgs: $.GenericTypeArgs | undefined, v: any): any {
return $.copyElement(v)
}

export async function main(): globalThis.Promise<void> {
// A generic function instantiated with several type arguments keeps them
// when it is used as a value, as in a call.
await embed(__goscriptStatic0(), "call")
let f: ((label: string) => void) | null = $.functionValue(async (label: string): globalThis.Promise<void> => await embed(__goscriptStatic0(), label), ({ kind: $.TypeKind.Function, params: [/* @__PURE__ */ $.basicType("string")], results: [] } as $.FunctionTypeInfo))
void (__goscriptAwait = f!("variable"), __goscriptAwait instanceof Promise ? await __goscriptAwait : __goscriptAwait)
let sims = $.makeMap<string, ((_p0: string) => void) | null>([["neuron", $.functionValue(async (label: string): globalThis.Promise<void> => await embed(__goscriptStatic0(), label), ({ kind: $.TypeKind.Function, params: [/* @__PURE__ */ $.basicType("string")], results: [] } as $.FunctionTypeInfo))]])
void (__goscriptAwait = $.mapGet<string, ((_p0: string) => void) | null, ((_p0: string) => void) | null>(sims, "neuron", null)[0]!("map"), __goscriptAwait instanceof Promise ? await __goscriptAwait : __goscriptAwait)

// So does one with a single type argument.
let g: ((v: string) => string | globalThis.Promise<string>) | null = $.functionValue((v: any): any => first(__goscriptStatic1(), v), ({ kind: $.TypeKind.Function, params: [{ kind: $.TypeKind.Interface, methods: [] }], results: [{ kind: $.TypeKind.Interface, methods: [] }] } as $.FunctionTypeInfo))
await $.println("single", (g!("x") as string))

// A function of the slices override, which takes no type arguments.
let index: ((s: $.Slice<number>, v: number) => number | globalThis.Promise<number>) | null = slices.Index
await $.println("slices", (index!($.arrayToSlice<number>([4, 5, 6]), 5) as number))
}

var __goscriptStatic0$value: any
function __goscriptStatic0(): any {
return __goscriptStatic0$value ??= ({[$.genericTypeArgsMarker]: $.genericTypeArgsBrand, S: { type: "main.Sim", zero: () => new Sim(true) }, C: { type: "main.Config", zero: () => new Config(true) }})
}

var __goscriptStatic1$value: any
function __goscriptStatic1(): any {
return __goscriptStatic1$value ??= ({[$.genericTypeArgsMarker]: $.genericTypeArgsBrand, T: { type: /* @__PURE__ */ $.basicType("string"), zero: () => "" }})
}

var __goscriptAwait: any

if ($.isMainScript(import.meta)) {
await main()
}
1 change: 1 addition & 0 deletions tests/tests/generic_func_value_type_args/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Config, Sim } from "./generic_func_value_type_args.gs.ts"
3 changes: 3 additions & 0 deletions tests/tests/generic_func_value_type_args/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type { Config } from "./lib.gs.ts"
export { BaseConfig, NewConfig } from "./lib.gs.ts"
import "./lib.gs.ts"
22 changes: 22 additions & 0 deletions tests/tests/generic_func_value_type_args/lib/lib.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package lib

// Config is implemented by pointers to configs that embed BaseConfig.
type Config interface {
AsBaseConfig() *BaseConfig
Defaults()
}

// BaseConfig is embedded in configs.
type BaseConfig struct {
Name string
}

func (bc *BaseConfig) AsBaseConfig() *BaseConfig { return bc }

// NewConfig makes a new config of type C, which needs its type argument.
func NewConfig[C any]() (*C, Config) {
cfgC := new(C)
cfg := any(cfgC).(Config)
cfg.Defaults()
return cfgC, cfg
}
55 changes: 55 additions & 0 deletions tests/tests/generic_func_value_type_args/lib/lib.gs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Generated file based on lib.go
// Updated when compliance tests are re-run, DO NOT EDIT!

import * as $ from "@goscript/builtin/index.js"

$.registerTypePackagePath("github.com/s4wave/goscript/tests/tests/generic_func_value_type_args/lib", ["lib.Config", "lib.BaseConfig"])

export type Config = {
AsBaseConfig(): BaseConfig | $.VarRef<BaseConfig> | null
Defaults(): void
}

$.registerInterfaceType(
"lib.Config",
null,
[{ name: "AsBaseConfig", args: [], returns: [{ type: /* @__PURE__ */ $.pointerType("lib.BaseConfig") }] }, { name: "Defaults", args: [], returns: [] }]
);

export class BaseConfig {
public declare Name: string

constructor(__goscriptValue?: boolean, Name$?: string) {
this.Name = Name$ ?? ""
;(this as any)[$.structValueMarker] = __goscriptValue === true
}

public clone(): BaseConfig {
return new BaseConfig(true, this.Name)
}

public __goscriptAssign(source: BaseConfig): this {
this.Name = source.Name
return this
}

public AsBaseConfig(): BaseConfig | $.VarRef<BaseConfig> | null {
const bc: BaseConfig | $.VarRef<BaseConfig> | null = this;
return bc
}

static __typeInfo = $.registerStructType(
"lib.BaseConfig",
() => new BaseConfig(),
() => [{ name: "AsBaseConfig", args: [], returns: [{ type: /* @__PURE__ */ $.pointerType("lib.BaseConfig") }], pointerReceiver: true }],
BaseConfig,
() => [{ name: "Name", key: "Name", type: /* @__PURE__ */ $.basicType("string") }]
)
}

export function NewConfig(__typeArgs: $.GenericTypeArgs | undefined): [any, Config | null] {
let cfgC = $.genericNew(__typeArgs, "C", null)
let cfg = $.mustTypeAssert<Config | null>($.typeParamPointerInterfaceValue(cfgC, __typeArgs?.["C"], "*C", /* @__PURE__ */ $.pointerType(__typeArgs?.["C"]?.type ?? { kind: $.TypeKind.Interface, methods: [] })), "lib.Config")
void (cfg !== null && (cfg as any).__isVarRef !== true && ((cfg as any).__goValue === undefined || (cfg as any).__goType?.charCodeAt(0) !== 42) ? (cfg as Exclude<Config, null>) : $.pointerValue<Exclude<Config, null>>(cfg)).Defaults()
return [cfgC, cfg]
}
27 changes: 27 additions & 0 deletions tests/tests/generic_func_value_type_args/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"compilerOptions": {
"paths": {
"*": [
"./*"
],
"@go/github.com/s4wave/goscript/*": [
"../../../*"
],
"@goscript/*": [
"../../../tests/tests/generic_func_value_type_args/run/output/@goscript/*",
"../../../gs/*",
"../../../tests/deps/*"
],
"@goscript/github.com/s4wave/goscript/tests/tests/generic_func_value_type_args/*": [
"./*"
]
}
},
"extends": "../../../tests/tsconfig.base.json",
"include": [
"generic_func_value_type_args.gs.ts",
"index.ts",
"lib/index.ts",
"lib/lib.gs.ts"
]
}
Loading