Skip to content

Commit

Permalink
Semantic: untyped closure exprs are checked at every call
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Mar 30, 2024
1 parent dec6df0 commit 4204e67
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 42 deletions.
2 changes: 1 addition & 1 deletion src/e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ pub fn main() {
println(foo(Foo(1, "y")))
}
fn foo(Foo(y ): Foo): String {
fn foo(Foo(y): Foo): String {
y
}`
}
Expand Down
78 changes: 42 additions & 36 deletions src/semantic/expr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
resolveType
} from '../typecheck/generic'
import { holeType, unitType, unknownType } from '../typecheck/type'
import { assert, todo, unreachable } from '../util/todo'
import { assert, unreachable } from '../util/todo'
import {
argCountMismatchError,
missingFieldsError,
Expand Down Expand Up @@ -390,15 +390,10 @@ export const checkMatchExpr = (matchExpr: MatchExpr, ctx: Context): void => {
* TODO: better error reporting when inferredType is provided
* TODO: closure generics
*/
export const checkClosureExpr = (
closureExpr: ClosureExpr,
ctx: Context,
caller?: AstNode<any>,
inferredType?: VirtualFnType
): void => {
export const checkClosureExpr = (closureExpr: ClosureExpr, ctx: Context): void => {
if (closureExpr.type && closureExpr.type.kind !== 'malleable-type') return

if (!inferredType && (!closureExpr.returnType || closureExpr.params.some(p => !!p.paramType))) {
if (!closureExpr.returnType || closureExpr.params.some(p => !!p.paramType)) {
// untyped closures concrete type is defined by its first usage
// malleable type is an indicator that concrete type is yet to be defined
closureExpr.type = { kind: 'malleable-type', operand: closureExpr }
Expand All @@ -410,32 +405,13 @@ export const checkClosureExpr = (
const module = ctx.moduleStack.at(-1)!
module.scopeStack.push({ kind: 'fn', definitions: new Map(), def: closureExpr, returns: [] })

if (caller && inferredType) {
if (closureExpr.params.length > inferredType.paramTypes.length) {
addError(ctx, argCountMismatchError(ctx, caller, closureExpr.params.length, inferredType.paramTypes.length))
return
}
for (let i = 0; i < closureExpr.params.length; i++) {
const param = closureExpr.params[i]
param.type = inferredType.paramTypes[i]
checkPattern(param.pattern, param.type, ctx)
}

closureExpr.type = {
kind: 'fn-type',
paramTypes: closureExpr.params.map(p => p.type!),
returnType: inferredType.returnType,
generics: []
}
} else {
closureExpr.params.forEach((p, i) => checkParam(p, i, ctx))
checkType(closureExpr.returnType!, ctx)
closureExpr.type = {
kind: 'fn-type',
paramTypes: closureExpr.params.map(p => typeToVirtual(p.paramType!, ctx)),
returnType: typeToVirtual(closureExpr.returnType!, ctx),
generics: []
}
closureExpr.params.forEach((p, i) => checkParam(p, i, ctx))
checkType(closureExpr.returnType!, ctx)
closureExpr.type = {
kind: 'fn-type',
paramTypes: closureExpr.params.map(p => typeToVirtual(p.paramType!, ctx)),
returnType: typeToVirtual(closureExpr.returnType!, ctx),
generics: []
}

checkBlock(closureExpr.block, ctx)
Expand All @@ -444,6 +420,37 @@ export const checkClosureExpr = (
module.scopeStack.pop()
}

export const checkResolvedClosureExpr = (
closureExpr: ClosureExpr,
ctx: Context,
caller: AstNode<any>,
inferredType: VirtualFnType
): VirtualType => {
if (closureExpr.params.length > inferredType.paramTypes.length) {
addError(ctx, argCountMismatchError(ctx, caller, closureExpr.params.length, inferredType.paramTypes.length))
return unknownType
}

const module = ctx.moduleStack.at(-1)!
module.scopeStack.push({ kind: 'fn', definitions: new Map(), def: closureExpr, returns: [] })

for (let i = 0; i < closureExpr.params.length; i++) {
const param = closureExpr.params[i]
param.type = inferredType.paramTypes[i]
checkPattern(param.pattern, param.type, ctx)
}

checkBlock(closureExpr.block, ctx)

module.scopeStack.pop()
return {
kind: 'fn-type',
paramTypes: closureExpr.params.map(p => p.type!),
returnType: closureExpr.block.type!,
generics: []
}
}

export const checkCall = (unaryExpr: UnaryExpr, ctx: Context): void => {
const call = <CallOp>unaryExpr.op
const operand = unaryExpr.operand
Expand Down Expand Up @@ -538,8 +545,7 @@ export const checkCall_ = (call: CallOp, operand: Operand, args: Expr[], ctx: Co
switch (operand.type.operand.kind) {
case 'closure-expr':
const closure = operand.type.operand
checkClosureExpr(closure, ctx, operand, closureType)
operand.type = closure.type
operand.type = checkResolvedClosureExpr(closure, ctx, operand, closureType)
break
default:
unreachable()
Expand Down
5 changes: 2 additions & 3 deletions src/semantic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import {
unspecifiedParamTypeError,
vidResolveToModuleError
} from './error'
import { checkClosureExpr, checkExpr } from './expr'
import { checkExpr, checkResolvedClosureExpr } from './expr'
import { checkPattern } from './match'
import { typeNames } from './type-def'
import { Upcast, makeUpcast, upcast } from './upcast'
Expand Down Expand Up @@ -780,8 +780,7 @@ export const checkCallArgs = (node: AstNode<any>, args: Operand[], paramTypes: V
switch (arg.type.operand.kind) {
case 'closure-expr':
const closure = arg.type.operand
checkClosureExpr(closure, ctx, node, paramType)
arg.type = closure.type
arg.type = checkResolvedClosureExpr(closure, ctx, node, paramType)
break
default:
unreachable()
Expand Down
6 changes: 4 additions & 2 deletions src/semantic/match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,10 @@ export const checkPattern = (
scope.definitions.set(defKey(nameDef), nameDef)
}

if (!isAssignable(expectedType, expr.type!, ctx)) {
addError(ctx, typeError(ctx, pattern.expr, expr.type!, expectedType))
if (expectedType.kind !== 'malleable-type') {
if (!isAssignable(expectedType, expr.type!, ctx)) {
addError(ctx, typeError(ctx, pattern.expr, expr.type!, expectedType))
}
}
}

Expand Down

0 comments on commit 4204e67

Please sign in to comment.