Skip to content

Commit

Permalink
Semantic: refactor upcast map layout
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Mar 25, 2024
1 parent a9ee5f1 commit 7c94922
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 42 deletions.
22 changes: 10 additions & 12 deletions src/codegen/js/expr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ export const emitExpr = (expr: Expr, module: Module, ctx: Context): EmitExpr =>

export const emitOperandExpr = (operandExpr: OperandExpr, module: Module, ctx: Context): EmitExpr => {
const operand = emitOperand(operandExpr.operand, module, ctx)
const upcasts = operandExpr.upcasts
const upcastEmit = upcasts ? emitUpcasts(operand.resultVar, upcasts) : undefined
const upcastEmit = emitUpcasts(operand.resultVar, operandExpr.upcasts)
return { emit: emitTree([operand.emit, upcastEmit]), resultVar: operand.resultVar }
}

Expand All @@ -52,13 +51,12 @@ export const emitUnaryExpr = (unaryExpr: UnaryExpr, module: Module, ctx: Context
methodDef.fn.static ? jsArgs.map(a => a.resultVar) : [lOp.resultVar, ...jsArgs.map(a => a.resultVar)]
).join(',')

const upcastEmit = upcasts ? emitUpcasts(lOp.resultVar, upcasts) : undefined
const upcastEmit = emitUpcasts(lOp.resultVar, upcasts)

const callerEmit = call.impl ? jsRelName(call.impl) : `${lOp.resultVar}.${relTypeName(methodDef.rel)}`
const callEmit = jsVariable(resultVar, emitToken(`${callerEmit}().${methodName}(${argsEmit})`))

const exprUpcasts = unaryExpr.upcasts
const exprUpcastEmit = exprUpcasts ? emitUpcasts(resultVar, exprUpcasts) : undefined
const exprUpcastEmit = emitUpcasts(resultVar, unaryExpr.upcasts)
return {
emit: emitTree(
[lOp.emit, emitTree(jsArgs.map(a => a.emit)), upcastEmit, callEmit, exprUpcastEmit],
Expand All @@ -69,7 +67,7 @@ export const emitUnaryExpr = (unaryExpr: UnaryExpr, module: Module, ctx: Context
}
case 'field-access-op': {
const lOp = emitOperand(unaryExpr.operand, module, ctx)
const upcastEmit = upcasts ? emitUpcasts(lOp.resultVar, upcasts) : undefined
const upcastEmit = emitUpcasts(lOp.resultVar, upcasts)
return {
emit: emitTree([
lOp.emit,
Expand All @@ -83,12 +81,12 @@ export const emitUnaryExpr = (unaryExpr: UnaryExpr, module: Module, ctx: Context
const call = unaryExpr.op
const args = call.args.map(a => {
const { emit, resultVar: res } = emitExpr(a.expr, module, ctx)
const upcastEmit = a.expr.upcasts ? emitUpcasts(res, a.expr.upcasts) : undefined
const upcastEmit = emitUpcasts(res, a.expr.upcasts)
return { emit: emitTree([emit, upcastEmit]), resultVar: res }
})
const genericTypes = call.generics?.map(g => emitGeneric(g, module, ctx)) ?? []
const jsArgs = [...args, ...genericTypes]
const upcastEmit = unaryExpr.upcasts ? emitUpcasts(resultVar, unaryExpr.upcasts) : undefined
const upcastEmit = emitUpcasts(resultVar, unaryExpr.upcasts)
const variantDef = call.variantDef
const parseNode = unaryExpr.operand.kind === 'identifier' ? unaryExpr.operand.parseNode : call.parseNode
if (variantDef) {
Expand All @@ -114,7 +112,7 @@ export const emitUnaryExpr = (unaryExpr: UnaryExpr, module: Module, ctx: Context
}
case 'unwrap-op': {
const operand = emitOperand(unaryExpr.operand, module, ctx)
const upcastEmit = upcasts ? emitUpcasts(operand.resultVar, upcasts) : undefined
const upcastEmit = emitUpcasts(operand.resultVar, upcasts)
return {
emit: emitTree([
operand.emit,
Expand All @@ -126,7 +124,7 @@ export const emitUnaryExpr = (unaryExpr: UnaryExpr, module: Module, ctx: Context
}
case 'bind-op': {
const operand = emitOperand(unaryExpr.operand, module, ctx)
const upcastEmit = upcasts ? emitUpcasts(operand.resultVar, upcasts) : undefined
const upcastEmit = emitUpcasts(operand.resultVar, upcasts)
const bindVar = nextVariable(ctx)
return {
emit: emitTree([
Expand Down Expand Up @@ -217,7 +215,7 @@ export const emitOperand = (operand: Operand, module: Module, ctx: Context): Emi
case 'for-expr': {
const expr = emitExpr(operand.expr, module, ctx)
const iteratorVar = nextVariable(ctx)
const upcastsEmit = operand.expr.upcasts ? emitUpcasts(expr.resultVar, operand.expr.upcasts) : undefined
const upcastsEmit = emitUpcasts(expr.resultVar, operand.expr.upcasts)
const iterator = {
emit: emitTree([
expr.emit,
Expand Down Expand Up @@ -314,7 +312,7 @@ export const emitOperand = (operand: Operand, module: Module, ctx: Context): Emi
}
const resultVar = operand.names.at(-1)!.value
const upcasts = operand.upcasts
const upcastEmit = upcasts ? emitUpcasts(resultVar, upcasts) : undefined
const upcastEmit = emitUpcasts(resultVar, upcasts)
return { emit: emitTree([upcastEmit]), resultVar }
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/codegen/js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,13 @@ const makeJsImport = (vid: VirtualIdentifier, importModule: Module, module: Modu
return { def, path: [...vid.names.slice(0, -1), ...(importModule.mod ? ['mod'] : [])].join('/') }
}

export const emitUpcasts = (resultVar: string, upcasts: Map<string, Upcast>): EmitToken => {
export const emitUpcasts = (resultVar: string, upcasts: Upcast[] | undefined): EmitToken | undefined => {
if (!upcasts || upcasts.length === 0) return undefined
const args = [...upcasts.entries()].map(
([, v]) => `[${[...v.traits.entries()].map(([tk, tv]) => `[${jsString(tk)}, ${jsRelName(tv)}]`).join(',')}]`
([, v]) =>
`[${Object.entries(v)
.map(([tk, tv]) => `[${jsString(tk)}, ${jsRelName(tv)}]`)
.join(',')}]`
)
args.unshift(resultVar)
return emitToken(`${resultVar}.upcast(${args.join(',')});`)
Expand Down
2 changes: 1 addition & 1 deletion src/scope/vid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export interface MethodDef {
kind: 'method-def'
fn: FnDef
rel: InstanceRelation
paramUpcasts?: (Map<string, Upcast> | undefined)[]
paramUpcasts?: (Upcast[] | undefined)[]
}

export interface VirtualIdentifierMatch<D = Definition> {
Expand Down
6 changes: 3 additions & 3 deletions src/semantic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import {
import { checkClosureExpr, checkExpr } from './expr'
import { checkPattern } from './match'
import { typeNames } from './type-def'
import { Upcast, makeUpcastMap, upcast } from './upcast'
import { Upcast, makeUpcasts, upcast } from './upcast'
import { VirtualUseExpr, useExprToVids } from './use-expr'

export interface Checked {
Expand All @@ -74,7 +74,7 @@ export interface Static {
}

export interface Virtual {
upcasts: Map<string, Upcast>
upcasts: Upcast[]
}

export const prepareModule = (module: Module): void => {
Expand Down Expand Up @@ -505,7 +505,7 @@ const checkImplDef = (implDef: ImplDef, ctx: Context) => {
m.paramUpcasts = m.fn.params.map(p => {
const genericMaps = [makeGenericMapOverStructure(implDef.rel!.forType, p.type!)]
const resolvedType = resolveType(p.type!, genericMaps, ctx)
return makeUpcastMap(resolvedType, m.rel.forType, ctx)
return makeUpcasts(resolvedType, m.rel.forType, ctx)
})
}
module.relImports.push(...implDef.superMethods.map(i => i.rel))
Expand Down
44 changes: 20 additions & 24 deletions src/semantic/upcast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,49 @@ import { InstanceRelation, relTypeName, resolveTypeImpl } from '../scope/trait'
import { VirtualType } from '../typecheck'
import { makeGenericMapOverStructure } from '../typecheck/generic'

export interface Upcast {
traits: Map<string, InstanceRelation>
}
export type Upcast = { [trait: string]: InstanceRelation }

export const upcast = (virtual: Partial<Virtual>, type: VirtualType, traitType: VirtualType, ctx: Context): void => {
const upcastMap = makeUpcastMap(type, traitType, ctx)
const upcastMap = makeUpcasts(type, traitType, ctx)
if (upcastMap) {
writeUpcast(virtual, upcastMap)
writeUpcasts(virtual, upcastMap)
}
}

export const makeUpcastMap = (
type: VirtualType,
traitType: VirtualType,
ctx: Context
): Map<string, Upcast> | undefined => {
export const makeUpcasts = (type: VirtualType, traitType: VirtualType, ctx: Context): Upcast[] => {
const res = resolveTypeImpl(type, traitType, ctx)
if (!res) return undefined
if (!res) return []
const genericMap = makeGenericMapOverStructure(type, res.impl.forType)
const upcasts = new Map()
upcasts.set('Self', { traits: new Map([[relTypeName(res.trait), res.impl]]) })
const upcasts = []
upcasts.push({ [relTypeName(res.trait)]: res.impl })
for (const g of res.impl.generics) {
const gUpcast: Upcast = { traits: new Map() }
const gUpcast: Upcast = {}
const concreteG = genericMap.get(g.name)
if (concreteG) {
for (const b of g.bounds) {
const gRes = resolveTypeImpl(concreteG, b, ctx)
if (gRes) {
gUpcast.traits.set(relTypeName(gRes.trait), gRes.impl)
gUpcast[relTypeName(gRes.trait)] = gRes.impl
ctx.moduleStack.at(-1)!.relImports.push(gRes.impl)
}
}
}
upcasts.set(g.name, gUpcast)
upcasts.push(gUpcast)
}
ctx.moduleStack.at(-1)!.relImports.push(res.impl)
return upcasts
}

const writeUpcast = (virtual: Partial<Virtual>, upcastMap: Map<string, Upcast>): void => {
virtual.upcasts ??= new Map()
upcastMap.forEach((up, k) => {
const existing = virtual.upcasts!.get(k)
if (existing) {
up.traits.forEach((t, tk) => existing.traits.set(tk, t))
const writeUpcasts = (virtual: Partial<Virtual>, upcasts: Upcast[]): void => {
virtual.upcasts ??= []
for (let i = 0; i < upcasts.length; i++) {
const up = upcasts[i]
if (virtual.upcasts.length > i) {
for (const [k, v] of Object.entries(up)) {
virtual.upcasts[i][k] = v
}
} else {
virtual.upcasts!.set(k, up)
virtual.upcasts.push(up)
}
})
}
}

0 comments on commit 7c94922

Please sign in to comment.