Skip to content

Commit

Permalink
Semantic: better missing method impls error
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Mar 16, 2024
1 parent 0b058c9 commit 9e980cd
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 15 deletions.
25 changes: 22 additions & 3 deletions src/semantic/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import { Type } from '../ast/type'
import { FieldDef } from '../ast/type-def'
import { Context } from '../scope'
import { vidToString } from '../scope/util'
import { VirtualType, virtualTypeToString } from '../typecheck'
import { MethodDef } from '../scope/vid'
import { VirtualFnType, VirtualType, virtualTypeToString } from '../typecheck'
import { unreachable } from '../util/todo'
import { MatchTree, unmatchedPaths } from './exhaust'

export interface SemanticError {
Expand Down Expand Up @@ -135,8 +137,25 @@ export const unexpectedPatternKindError = (ctx: Context, param: Param): Semantic
return semanticError(16, ctx, param.pattern, 'unexpected pattern type', notes)
}

export const missingMethodImplError = (ctx: Context, implDef: ImplDef, methodVid: string): SemanticError => {
const msg = `missing method implementation \`${methodVid}\``
export const missingMethodImplsError = (ctx: Context, implDef: ImplDef, methodDefs: MethodDef[]): SemanticError => {
const printParam = (param: Param, i: number): string => {
const type = param.paramType ? `: ${virtualTypeToString(param.type!)}` : ''
switch (param.pattern.expr.kind) {
case 'name':
return `${param.pattern.expr.value}${type}`
case 'con-pattern':
return `p${i}${type}`
case 'hole':
return `_${type}`
}
return unreachable()
}
const printFn = (fnDef: FnDef): string => {
const returnType = fnDef.returnType ? `: ${virtualTypeToString((<VirtualFnType>fnDef.type!).returnType)}` : ''
return ` fn ${fnDef.name.value}(${fnDef.params.map(printParam).join(', ')})${returnType}`
}
const methodSigs = methodDefs.map(m => printFn(m.fn)).join('\n')
const msg = `missing implementations for methods:\n${methodSigs}`
return semanticError(17, ctx, implDef.identifier.names.at(-1)!, msg)
}

Expand Down
13 changes: 8 additions & 5 deletions src/semantic/expr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,11 +303,14 @@ export const checkForExpr = (forExpr: ForExpr, ctx: Context): void => {
.map(t => extractConcreteSupertype(forExpr.expr.type!, t.identifier, ctx))
.filter(t => !!t)
.at(0)
assert(!!iterableType, 'unresolved iterable type')
assert(iterableType!.kind === 'vid-type', `iterable type is ${iterableType!.kind}`)
const itemType = (<VidType>iterableType).typeArgs.at(0)
assert(!!itemType, 'unresolved item type')
checkPattern(forExpr.pattern, itemType!, ctx)
if (iterableType) {
assert(iterableType!.kind === 'vid-type', `iterable type is ${iterableType!.kind}`)
const itemType = (<VidType>iterableType).typeArgs.at(0)
assert(!!itemType, 'unresolved item type')
checkPattern(forExpr.pattern, itemType!, ctx)
} else {
addError(ctx, notIterableError(ctx, forExpr.expr))
}

const abr = checkBlock(forExpr.block, ctx)
assert(!!forExpr.block.type)
Expand Down
13 changes: 6 additions & 7 deletions src/semantic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
duplicateImportError,
expectedTraitError,
methodNotDefinedError,
missingMethodImplError,
missingMethodImplsError,
noBodyFnError,
notFoundError,
notInFnScopeError,
Expand Down Expand Up @@ -470,12 +470,11 @@ const checkImplDef = (implDef: ImplDef, ctx: Context) => {
})
const requiredImplMethods = traitMethods.filter(f => !f.fn.block)
const implMethods = implDef.block.statements.filter(s => s.kind === 'fn-def').map(s => <FnDef>s)
for (const m of requiredImplMethods) {
const mName = m.fn.name.value
const mVid = `${vidToString(ref.vid)}::${mName}`
if (!implMethods.find(im => im.name.value === mName)) {
addError(ctx, missingMethodImplError(ctx, implDef, mVid))
}
const missingImpls = requiredImplMethods.filter(
m => !implMethods.find(im => im.name.value === m.fn.name.value)
)
if (missingImpls.length > 0) {
addError(ctx, missingMethodImplsError(ctx, implDef, missingImpls))
}
for (const m of implMethods) {
const mName = m.name.value
Expand Down

0 comments on commit 9e980cd

Please sign in to comment.