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
2 changes: 1 addition & 1 deletion compiler/compiler-cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const compilerCacheSchema = "goscript-package-artifact-v1"
// compilerSemanticsVersion versions emitted-output semantics. Bump this value
// with every behavior-changing compiler commit so artifacts cached by an
// older binary miss and rebuild instead of replaying stale bytes.
const compilerSemanticsVersion = "1"
const compilerSemanticsVersion = "2"

type compilerCacheEntryKind string

Expand Down
1 change: 0 additions & 1 deletion compiler/gotest/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,6 @@ func packageExecutionFanout(
var group errgroup.Group
group.SetLimit(max(parallelism, 1))
for _, idx := range indexes {
idx := idx
group.Go(func() error {
select {
case <-ctx.Done():
Expand Down
44 changes: 40 additions & 4 deletions compiler/lowering.go
Original file line number Diff line number Diff line change
Expand Up @@ -11745,6 +11745,7 @@ func (o *LoweringOwner) lowerValueForTargetTypes(
}
return value
}

func (o *LoweringOwner) lowerInterfaceValueExpr(
ctx lowerFileContext,
targetType types.Type,
Expand Down Expand Up @@ -13769,12 +13770,15 @@ func (o *LoweringOwner) genericTypeArgsExpr(ctx lowerFileContext, callee ast.Exp
return "undefined"
}
typeParams := signature.TypeParams()
instanceArgs := instantiatedTypeArgs(ctx, callee)
entries := make([]string, 0, typeParams.Len())
for idx := range typeParams.Len() {
if idx >= len(typeArgExprs) {
break
var typ types.Type
if idx < len(typeArgExprs) {
typ = ctx.semPkg.source.TypesInfo.TypeOf(typeArgExprs[idx])
} else if instanceArgs != nil && idx < instanceArgs.Len() {
typ = instanceArgs.At(idx)
}
typ := ctx.semPkg.source.TypesInfo.TypeOf(typeArgExprs[idx])
if typ == nil {
continue
}
Expand All @@ -13786,6 +13790,30 @@ func (o *LoweringOwner) genericTypeArgsExpr(ctx lowerFileContext, callee ast.Exp
return o.genericTypeArgsLiteral(entries)
}

// instantiatedTypeArgs resolves the full type argument list recorded for an
// explicit generic instantiation, including trailing arguments Go inferred
// through constraint core types rather than explicit syntax.
func instantiatedTypeArgs(ctx lowerFileContext, callee ast.Expr) *types.TypeList {
if ctx.semPkg == nil || ctx.semPkg.source == nil {
return nil
}
instances := ctx.semPkg.source.TypesInfo.Instances
if instances == nil {
return nil
}
switch typed := callee.(type) {
case *ast.Ident:
if instance, ok := instances[typed]; ok {
return instance.TypeArgs
}
case *ast.SelectorExpr:
if instance, ok := instances[typed.Sel]; ok {
return instance.TypeArgs
}
}
return nil
}

func (o *LoweringOwner) inferredGenericTypeArgsExpr(
ctx lowerFileContext,
signature *types.Signature,
Expand Down Expand Up @@ -13924,12 +13952,20 @@ func (o *LoweringOwner) genericMethodDescriptorsForType(
}
genericArgs := o.genericReceiverTypeArgsExprForMethod(ctx, method, methodSetType)
if namedStructType(named) != nil || isInterfaceType(named) {
// A struct receiver may arrive as a VarRef when the caller
// converted the address of a variable to the constraint's
// pointer type-set element, so unwrap it before the
// property-keyed call. Instances pass through unchanged.
receiverExpr := "receiver"
if namedStructType(named) != nil {
receiverExpr = o.runtimeOwner.QualifiedHelper(RuntimeHelperPointerValue) + "(receiver)"
}
callArgs := make([]string, 0, 2)
if genericArgs != "" {
callArgs = append(callArgs, genericArgs)
}
callArgs = append(callArgs, "..."+o.runtimeOwner.QualifiedHelper(RuntimeHelperStripGenericTypeArgs)+"(args)")
methods = append(methods, method.Name()+": (receiver: any, ...args: any[]) => receiver."+method.Name()+"("+strings.Join(callArgs, ", ")+")")
methods = append(methods, method.Name()+": (receiver: any, ...args: any[]) => "+receiverExpr+"."+method.Name()+"("+strings.Join(callArgs, ", ")+")")
continue
}
receiver := "receiver"
Expand Down
2 changes: 1 addition & 1 deletion tests/tests/factory_async_callback/dep/dep.gs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,5 @@ export async function NewFactory(b: Bus): globalThis.Promise<Factory | null> {
let ch: $.Channel<{}> | null = $.makeChannel<{}>(1, {}, "both")
await $.chanSend(ch, {})
await $.chanRecv(ch)
return $.namedValueInterfaceValue<Factory | null>(new factory(), "*dep.factory", {GetConfigID: (receiver: any, ...args: any[]) => receiver.GetConfigID({[$.genericTypeArgsMarker]: true, T: { type: "dep.Bus", zero: () => $.markAsStructValue(new Bus()) }}, ...$.stripGenericTypeArgs(args))}, { kind: $.TypeKind.Pointer, elemType: "dep.factory" }, [{ name: "GetConfigID", args: [], returns: [{ name: "_r0", type: { kind: $.TypeKind.Basic, name: "string" } }] }])
return $.namedValueInterfaceValue<Factory | null>(new factory(), "*dep.factory", {GetConfigID: (receiver: any, ...args: any[]) => $.pointerValue(receiver).GetConfigID({[$.genericTypeArgsMarker]: true, T: { type: "dep.Bus", zero: () => $.markAsStructValue(new Bus()) }}, ...$.stripGenericTypeArgs(args))}, { kind: $.TypeKind.Pointer, elemType: "dep.factory" }, [{ name: "GetConfigID", args: [], returns: [{ name: "_r0", type: { kind: $.TypeKind.Basic, name: "string" } }] }])
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export async function cloneSlice<T>(__typeArgs: $.GenericTypeArgs | undefined, i

export async function main(): globalThis.Promise<void> {
let items: $.Slice<item | $.VarRef<item> | null> = $.arrayToSlice<item | $.VarRef<item> | null>([new item({value: "first"}), new item({value: "second"})])
let cloned: $.Slice<item | $.VarRef<item> | null> = (await cloneSlice({[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Pointer, elemType: "main.item" }, zero: () => null, methods: {CloneVT: (receiver: any, ...args: any[]) => receiver.CloneVT(...$.stripGenericTypeArgs(args))} }}, items) as $.Slice<item | $.VarRef<item> | null>)
let cloned: $.Slice<item | $.VarRef<item> | null> = (await cloneSlice({[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Pointer, elemType: "main.item" }, zero: () => null, methods: {CloneVT: (receiver: any, ...args: any[]) => $.pointerValue(receiver).CloneVT(...$.stripGenericTypeArgs(args))} }}, items) as $.Slice<item | $.VarRef<item> | null>)
await $.println($.len(cloned), $.pointerValue<item>($.arrayIndex(cloned!, 0)).value, $.pointerValue<item>($.arrayIndex(cloned!, 1)).value, $.pointerEqual($.arrayIndex(cloned!, 0), $.arrayIndex(items!, 0)))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,10 @@ export async function main(): globalThis.Promise<void> {
let bt: blockType | $.VarRef<blockType> | null = (NewBlockType(undefined, "sample", $.functionValue((): sampleBlock | $.VarRef<sampleBlock> | null => {
return new sampleBlock()
}, ({ kind: $.TypeKind.Function, params: [], results: [{ kind: $.TypeKind.Pointer, elemType: "main.sampleBlock" }] } as $.FunctionTypeInfo))) as blockType | $.VarRef<blockType> | null)
let blk = await blockType.prototype.Constructor.call(bt, {[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Pointer, elemType: "main.sampleBlock" }, zero: () => null, methods: {MarshalBlock: (receiver: any, ...args: any[]) => receiver.MarshalBlock(...$.stripGenericTypeArgs(args)), UnmarshalBlock: (receiver: any, ...args: any[]) => receiver.UnmarshalBlock(...$.stripGenericTypeArgs(args))} }})
let blk = await blockType.prototype.Constructor.call(bt, {[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Pointer, elemType: "main.sampleBlock" }, zero: () => null, methods: {MarshalBlock: (receiver: any, ...args: any[]) => $.pointerValue(receiver).MarshalBlock(...$.stripGenericTypeArgs(args)), UnmarshalBlock: (receiver: any, ...args: any[]) => $.pointerValue(receiver).UnmarshalBlock(...$.stripGenericTypeArgs(args))} }})
let __goscriptTuple0: any = await $.pointerValue<Exclude<Block, null>>(blk).MarshalBlock()
let data: $.Slice<number> = __goscriptTuple0[0]
await $.println(blockType.prototype.GetBlockTypeID.call(bt, {[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Pointer, elemType: "main.sampleBlock" }, zero: () => null, methods: {MarshalBlock: (receiver: any, ...args: any[]) => receiver.MarshalBlock(...$.stripGenericTypeArgs(args)), UnmarshalBlock: (receiver: any, ...args: any[]) => receiver.UnmarshalBlock(...$.stripGenericTypeArgs(args))} }}), $.len(data))
await $.println(blockType.prototype.GetBlockTypeID.call(bt, {[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Pointer, elemType: "main.sampleBlock" }, zero: () => null, methods: {MarshalBlock: (receiver: any, ...args: any[]) => $.pointerValue(receiver).MarshalBlock(...$.stripGenericTypeArgs(args)), UnmarshalBlock: (receiver: any, ...args: any[]) => $.pointerValue(receiver).UnmarshalBlock(...$.stripGenericTypeArgs(args))} }}), $.len(data))
}

if ($.isMainScript(import.meta)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export function setConcrete(w: dep.Wrapper | $.VarRef<dep.Wrapper> | null): stri

export async function main(): globalThis.Promise<void> {
let impl: wrapper | $.VarRef<wrapper> | null = new wrapper({Impl: new dep.Impl({Item: 7})})
await $.println("interface:", await read({[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Basic, name: "int" }, zero: () => 0 }, E: { type: { kind: $.TypeKind.Basic, name: "string" }, zero: () => "" }}, $.namedValueInterfaceValue<Derived | null>(impl, "*main.wrapper", {Other: (receiver: any, ...args: any[]) => receiver.Other({[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Basic, name: "int" }, zero: () => 0 }, E: { type: { kind: $.TypeKind.Basic, name: "string" }, zero: () => "" }}, ...$.stripGenericTypeArgs(args)), Value: (receiver: any, ...args: any[]) => receiver.Value({[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Basic, name: "int" }, zero: () => 0 }}, ...$.stripGenericTypeArgs(args))}, { kind: $.TypeKind.Pointer, elemType: "main.wrapper" }, [{ name: "Other", args: [], returns: [{ name: "_r0", type: { kind: $.TypeKind.Basic, name: "string" } }] }, { name: "Value", args: [], returns: [{ name: "_r0", type: { kind: $.TypeKind.Basic, name: "int" } }] }])))
await $.println("interface:", await read({[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Basic, name: "int" }, zero: () => 0 }, E: { type: { kind: $.TypeKind.Basic, name: "string" }, zero: () => "" }}, $.namedValueInterfaceValue<Derived | null>(impl, "*main.wrapper", {Other: (receiver: any, ...args: any[]) => $.pointerValue(receiver).Other({[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Basic, name: "int" }, zero: () => 0 }, E: { type: { kind: $.TypeKind.Basic, name: "string" }, zero: () => "" }}, ...$.stripGenericTypeArgs(args)), Value: (receiver: any, ...args: any[]) => $.pointerValue(receiver).Value({[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Basic, name: "int" }, zero: () => 0 }}, ...$.stripGenericTypeArgs(args))}, { kind: $.TypeKind.Pointer, elemType: "main.wrapper" }, [{ name: "Other", args: [], returns: [{ name: "_r0", type: { kind: $.TypeKind.Basic, name: "string" } }] }, { name: "Value", args: [], returns: [{ name: "_r0", type: { kind: $.TypeKind.Basic, name: "int" } }] }])))
let keyed: dep.Wrapper | $.VarRef<dep.Wrapper> | null = new dep.Wrapper({Keyed: new dep.Keyed()})
await $.println("generic empty:", $.stringEqual(setGeneric({[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Basic, name: "int" }, zero: () => 0 }, U: { type: { kind: $.TypeKind.Basic, name: "string" }, zero: () => "" }}, keyed, 7), ""))
await $.println("concrete empty:", $.stringEqual(setConcrete(keyed), ""))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export class genericValue {
}

export async function main(): globalThis.Promise<void> {
let resolver: Resolver | null = $.namedValueInterfaceValue<Resolver | null>(new genericResolver(), "*main.genericResolver", {Resolve: (receiver: any, ...args: any[]) => receiver.Resolve({[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Basic, name: "int" }, zero: () => 0 }}, ...$.stripGenericTypeArgs(args))}, { kind: $.TypeKind.Pointer, elemType: "main.genericResolver" }, [{ name: "Resolve", args: [{ name: "ctx", type: "context.Context" }, { name: "handler", type: "main.Handler" }], returns: [{ name: "_r0", type: "error" }] }])
let resolver: Resolver | null = $.namedValueInterfaceValue<Resolver | null>(new genericResolver(), "*main.genericResolver", {Resolve: (receiver: any, ...args: any[]) => $.pointerValue(receiver).Resolve({[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Basic, name: "int" }, zero: () => 0 }}, ...$.stripGenericTypeArgs(args))}, { kind: $.TypeKind.Pointer, elemType: "main.genericResolver" }, [{ name: "Resolve", args: [{ name: "ctx", type: "context.Context" }, { name: "handler", type: "main.Handler" }], returns: [{ name: "_r0", type: "error" }] }])
{
let err = await $.pointerValue<Exclude<Resolver, null>>(resolver).Resolve(context.Background(), $.interfaceValue<Handler | null>($.markAsStructValue(new handler()), "main.handler", "main.handler"))
if (err != null) {
Expand All @@ -148,7 +148,7 @@ export async function main(): globalThis.Promise<void> {
}
await $.println("resolve ok")
let g = $.markAsStructValue(new genericValue({value: 7}))
let reader: ValueReader | null = $.namedValueInterfaceValue<ValueReader | null>($.markAsStructValue($.cloneStructValue(g)), "main.genericValue", {Get: (receiver: any, ...args: any[]) => receiver.Get({[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Basic, name: "int" }, zero: () => 0 }}, ...$.stripGenericTypeArgs(args))}, "main.genericValue", [{ name: "Get", args: [], returns: [{ name: "_r0", type: { kind: $.TypeKind.Basic, name: "int" } }] }])
let reader: ValueReader | null = $.namedValueInterfaceValue<ValueReader | null>($.markAsStructValue($.cloneStructValue(g)), "main.genericValue", {Get: (receiver: any, ...args: any[]) => $.pointerValue(receiver).Get({[$.genericTypeArgsMarker]: true, T: { type: { kind: $.TypeKind.Basic, name: "int" }, zero: () => 0 }}, ...$.stripGenericTypeArgs(args))}, "main.genericValue", [{ name: "Get", args: [], returns: [{ name: "_r0", type: { kind: $.TypeKind.Basic, name: "int" } }] }])
g.value = 9
if (await $.pointerValue<Exclude<ValueReader, null>>(reader).Get() != 7) {
await $.println("value copy failed")
Expand Down
Loading
Loading