diff --git a/src/semantic/error.ts b/src/semantic/error.ts index cd7f3919..e0f5f99d 100644 --- a/src/semantic/error.ts +++ b/src/semantic/error.ts @@ -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 { @@ -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((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) } diff --git a/src/semantic/expr.ts b/src/semantic/expr.ts index cd921839..1d68e15b 100644 --- a/src/semantic/expr.ts +++ b/src/semantic/expr.ts @@ -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 = (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 = (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) diff --git a/src/semantic/index.ts b/src/semantic/index.ts index 3b4d4572..79fc450e 100644 --- a/src/semantic/index.ts +++ b/src/semantic/index.ts @@ -30,7 +30,7 @@ import { duplicateImportError, expectedTraitError, methodNotDefinedError, - missingMethodImplError, + missingMethodImplsError, noBodyFnError, notFoundError, notInFnScopeError, @@ -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 => 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