diff --git a/src/ast/operand.ts b/src/ast/operand.ts index 3e5e0a5c..b1fe44fb 100644 --- a/src/ast/operand.ts +++ b/src/ast/operand.ts @@ -1,6 +1,7 @@ import { LexerToken } from '../lexer/lexer' import { ParseNode, ParseTree, filterNonAstNodes } from '../parser' import { nameLikeTokens } from '../parser/fns' +import { InstanceRelation } from '../scope/trait' import { VirtualIdentifierMatch } from '../scope/vid' import { Typed } from '../semantic' import { Expr, buildExpr } from './expr' @@ -9,7 +10,7 @@ import { MatchExpr, Pattern, buildMatchExpr, buildNumber, buildPattern } from '. import { Block, buildBlock } from './statement' import { Type, buildType } from './type' -export type Operand = +export type Operand = ( | IfExpr | IfLetExpr | WhileExpr @@ -24,6 +25,7 @@ export type Operand = | FloatLiteral | BoolLiteral | Identifier +) & { traits?: Map } export const buildOperand = (node: ParseNode): Operand => { const n = filterNonAstNodes(node)[0] diff --git a/src/codegen/js/expr.ts b/src/codegen/js/expr.ts index bc6a6686..1a537ad6 100644 --- a/src/codegen/js/expr.ts +++ b/src/codegen/js/expr.ts @@ -38,7 +38,14 @@ export const emitUnaryExpr = (unaryExpr: UnaryExpr, module: Module, ctx: Context const resultVar = nextVariable(ctx) switch (unaryExpr.op.kind) { case 'call-op': - const args = unaryExpr.op.args.map(a => emitExpr(a.expr, module, ctx)) + const args = unaryExpr.op.args.map(a => { + const { emit, resultVar: res } = emitExpr(a.expr, module, ctx) + const traits = a.expr.kind === 'operand-expr' ? a.expr.operand.traits : undefined + const traitEmit = traits + ? [...traits.entries()].map(([name, rel]) => `${a}.${name} = ${jsRelName(rel)}`) + : '' + return { emit: emitLines([emit, ...traitEmit]), resultVar: res } + }) const genericTypes = unaryExpr.op.generics?.map(g => emitGeneric(g, module, ctx)) ?? [] const jsArgs = [...args, ...genericTypes] const variantDef = unaryExpr.op.variantDef @@ -89,14 +96,19 @@ export const emitBinaryExpr = (binaryExpr: BinaryExpr, module: Module, ctx: Cont ? jsArgs.map(a => a.resultVar) : [lOp.resultVar, ...jsArgs.map(a => a.resultVar)] ).join(', ') + const upcastRels = binaryExpr.lOperand.traits + const upcastEmit = upcastRels + ? [...upcastRels.entries()].map(([name, rel]) => `${lOp.resultVar}.${name} = ${jsRelName(rel)}`) + : '' return { emit: emitLines([ lOp.emit, + ...upcastEmit, emitLines(jsArgs.map(a => a.emit)), jsVariable( resultVar, `${ - call.impl ? jsRelName(call.impl) : jsError('dynamic dispatch') + call.impl ? jsRelName(call.impl) : `${lOp.resultVar}.${relTypeName(methodDef.rel)}` }().${methodName}(${argsEmit})` ) ]), diff --git a/src/scope/trait.ts b/src/scope/trait.ts index 748f3861..e0872fc2 100644 --- a/src/scope/trait.ts +++ b/src/scope/trait.ts @@ -209,7 +209,7 @@ export const relTypeName = (rel: InstanceRelation): string => { export const resolveGenericImpls = (generic: VirtualGeneric, ctx: Context): InstanceRelation[] => { return generic.bounds.flatMap(b => { const candidates = ctx.impls - .filter(i => i.instanceDef.kind === 'impl-def' && isAssignable(b, i.implType, ctx)) + .filter(i => isAssignable(b, i.implType, ctx)) .toSorted((a, b) => relOrdering(b) - relOrdering(a)) return candidates.length > 0 ? [candidates.at(0)!] : [] }) @@ -219,7 +219,10 @@ export const resolveMethodImpl = (type: VirtualType, method: MethodDef, ctx: Con const candidates = ctx.impls .filter( i => - i.instanceDef.kind === 'impl-def' && + (i.instanceDef.kind === 'impl-def' || + i.instanceDef.block.statements.find( + s => s.kind === 'fn-def' && s.name.value === method.fn.name.value && s.block + )) && isAssignable(type, i.forType, ctx) && isAssignable(i.implType, method.rel.implType, ctx) && (!i.inherent || @@ -227,11 +230,11 @@ export const resolveMethodImpl = (type: VirtualType, method: MethodDef, ctx: Con s => s.kind === 'fn-def' && s.name.value === method.fn.name.value )) ) - .toSorted((a, b) => relOrdering(b) - relOrdering(a)) + .toSorted((a, b) => relOrdering(b, type, method.rel.forType) - relOrdering(a, type, method.rel.forType)) return candidates.at(0) } -export const relOrdering = (rel: InstanceRelation): number => { +export const relOrdering = (rel: InstanceRelation, implType?: VirtualType, forType?: VirtualType): number => { let score = 0 if (rel.instanceDef.kind === 'impl-def') score += 8 return score diff --git a/src/util/array.ts b/src/util/array.ts index f04ef6b7..63eeccd7 100644 --- a/src/util/array.ts +++ b/src/util/array.ts @@ -42,3 +42,13 @@ export const groupByHaving = (arr: T[], keyFn: (t: T) => K, havingFn: (t: } return groups } + +export const zip = (a: A[], b: B[], f: (a: A, b: B, i: number) => C): C[] => { + const la = a.length + const lb = b.length + const res = [] + for (let i = 0; i < la && i < lb; i++) { + res.push(f(a[i], b[i], i)) + } + return res +}