From 903d278d00401554175f2e5510031488f12c2112 Mon Sep 17 00:00:00 2001 From: ivanjermakov Date: Wed, 11 Sep 2024 01:56:13 +0200 Subject: [PATCH] Type bounds: get rid of `def` type --- src/phase/std-type.ts | 3 +- src/phase/top-scope-type.ts | 11 +++++--- src/phase/type-unify.ts | 56 +++++++++++++------------------------ src/semantic/impl.ts | 2 +- src/typecheck/index.ts | 10 +------ 5 files changed, 30 insertions(+), 52 deletions(-) diff --git a/src/phase/std-type.ts b/src/phase/std-type.ts index 1a3757a..5554575 100644 --- a/src/phase/std-type.ts +++ b/src/phase/std-type.ts @@ -3,7 +3,6 @@ import { Identifier } from '../ast/operand' import { findName } from '../phase/name-resolve' import { Context, addError, idFromString } from '../scope' import { notFoundError } from '../semantic/error' -import { makeDefType } from '../typecheck' export type StdTypeIds = { unit?: Identifier @@ -55,7 +54,7 @@ export const setStdTypeIds = (node: AstNode, ctx: Context): void => { return } id.def = def - id.type = makeDefType(def) + id.type = id ctx.stdTypeIds[name] = id }) } diff --git a/src/phase/top-scope-type.ts b/src/phase/top-scope-type.ts index 6c0cb2a..103b18f 100644 --- a/src/phase/top-scope-type.ts +++ b/src/phase/top-scope-type.ts @@ -1,7 +1,7 @@ import { AstNode } from '../ast' import { Identifier } from '../ast/operand' import { Context } from '../scope' -import { makeDefType, makeErrorType, makeTypeParam } from '../typecheck' +import { makeErrorType, makeTypeParam } from '../typecheck' import { assert, todo, unreachable } from '../util/todo' /** @@ -14,12 +14,10 @@ export const setTopScopeDefType = (node: AstNode, ctx: Context) => { break } case 'trait-def': { - node.type = makeDefType(node) node.generics.forEach(g => setTopScopeDefType(g, ctx)) break } case 'type-def': { - node.type = makeDefType(node) node.generics.forEach(g => setTopScopeDefType(g, ctx)) break } @@ -72,7 +70,12 @@ export const setTopScopeType = (node: AstNode, ctx: Context) => { kind: 'identifier', parseNode: node.parseNode, names: [node.typeDef!.name], - typeArgs: node.typeDef!.generics.map(g => ({ kind: 'identifier', names: [g.name], typeArgs: [] })), + typeArgs: node.typeDef!.generics.map(g => ({ + kind: 'identifier', + names: [g.name], + typeArgs: [], + def: g + })), def: node.typeDef } const fnType = { diff --git a/src/phase/type-unify.ts b/src/phase/type-unify.ts index 7b58323..9d93287 100644 --- a/src/phase/type-unify.ts +++ b/src/phase/type-unify.ts @@ -17,7 +17,7 @@ import { } from '../typecheck' import { zip } from '../util/array' import { assign } from '../util/object' -import { todo, unreachable } from '../util/todo' +import { unreachable } from '../util/todo' /** * Unify type bounds @@ -256,8 +256,7 @@ export const unifyType = (type: InferredType, ctx: Context): void => { break } case 'identifier': - if (type.def?.type && type.typeArgs.length === 0) { - type.typeArgs.forEach(ta => unifyType(ta, ctx)) + if (type.def?.type?.kind === 'type-param') { assign(type, type.def.type) break } @@ -266,7 +265,6 @@ export const unifyType = (type: InferredType, ctx: Context): void => { case 'name': case 'type-param': case 'hole': - case 'def': case 'error': break } @@ -307,19 +305,31 @@ const unify_ = (a: InferredType, b: InferredType, ctx: Context, stack: [string, } break } - case 'def': { + case 'identifier': { if (a.def === ctx.stdTypeIds.never?.def) { return b } switch (b.kind) { // biome-ignore lint: - case 'def': - // TODO: respect def's trait impls - if (b.kind === 'def' && a.def === b.def) { - return a + case 'identifier': { + if (a.def && a.def === b.def) { + if (a.typeArgs.length === b.typeArgs.length) { + const typeArgs = ( + zip(a.typeArgs, b.typeArgs, (ta, tb) => unify(ta, tb, ctx, stack)) + ) + const u: Identifier = { + kind: 'identifier', + parseNode: a.parseNode, + names: a.names, + typeArgs + } + assign(a, u) + assign(b, u) + return u + } } + } case 'inferred-fn': - case 'identifier': case 'fn-type': case 'name': const e = makeErrorType( @@ -353,24 +363,6 @@ const unify_ = (a: InferredType, b: InferredType, ctx: Context, stack: [string, } case 'name': break - case 'identifier': - if (b.kind === 'identifier') { - if (b.def?.type?.kind === 'type-param') { - return unify(b.def.type, a, ctx, stack) - } - if (a.def && a.def === b.def) { - if (a.typeArgs.length !== b.typeArgs.length) { - todo() - break - } - const typeArgs = zip(a.typeArgs, b.typeArgs, (ta, tb) => unify(ta, tb, ctx, stack)) - const u: Identifier = { kind: 'identifier', parseNode: a.parseNode, names: a.names, typeArgs } - assign(a, u) - assign(b, u) - return u - } - } - break case 'hole': return b case 'error': @@ -407,12 +399,6 @@ const extractReturnType = (type: InferredType, ctx: Context): InferredType | und return undefined case 'error': return type - case 'def': - if (type.def.kind === 'fn-def') { - unreachable() - return type.def.returnType! - } - return undefined case 'method-call': case 'fn-type': case 'return': @@ -447,8 +433,6 @@ const extractDefs = (t: InferredType): Definition[] => { return [t.def] } break - case 'def': - return [t.def] case 'type-param': return t.type.bounds.map(b => b.def) } diff --git a/src/semantic/impl.ts b/src/semantic/impl.ts index 8978cec..93fd56d 100644 --- a/src/semantic/impl.ts +++ b/src/semantic/impl.ts @@ -12,7 +12,7 @@ export const findMethodDefForMethodCall = (type: InferredType, ctx: Context): Fn assert(false, type.kind) return unreachable() } - if (type.operandType.kind === 'def') { + if (type.operandType.kind === 'identifier') { const def = type.operandType.def const impls = ctx.packages.flatMap(p => p.modules.flatMap(m => m.impls).filter(impl => impl.forTrait && impl.forTrait.def === def) diff --git a/src/typecheck/index.ts b/src/typecheck/index.ts index 6888842..c77871e 100644 --- a/src/typecheck/index.ts +++ b/src/typecheck/index.ts @@ -2,7 +2,7 @@ import { UnaryExpr } from '../ast/expr' import { FieldAccessOp, MethodCallOp } from '../ast/op' import { Name } from '../ast/operand' import { Generic, Type } from '../ast/type' -import { Context, Definition, defKey, idToString } from '../scope' +import { Context, idToString } from '../scope' import { assert, unreachable } from '../util/todo' export type InferredType = @@ -23,7 +23,6 @@ export type InferredType = } | { kind: 'field-access'; operandType: InferredType; fieldName: Name } | { kind: 'method-call'; operandType: InferredType; op: MethodCallOp } - | { kind: 'def'; def: Definition } | Type | { kind: 'return'; type: InferredType } | ErrorType @@ -71,8 +70,6 @@ export const makeMethodCallType = (expr: UnaryExpr) => ({ export const makeReturnType = (type: InferredType) => ({ kind: 'return', type }) -export const makeDefType = (def: Definition) => ({ kind: 'def', def }) - export const makeErrorType = (message?: string, errorKind: ErrorTypeKind = 'other') => ({ kind: 'error', error: { @@ -99,9 +96,6 @@ export const instantiateDefType = (t: InferredType, ctx: Context): InferredType } ]) } - case 'def': { - return makeInferredType([t]) - } default: return t } @@ -126,8 +120,6 @@ export const inferredTypeToString = (t: InferredType, depth = 0): string => { )}` case 'return': return `ret(${inferredTypeToString(t.type, depth + 1)})` - case 'def': - return `def(${t.def.kind} ${defKey(t.def)})` case 'field-access': return `(${inferredTypeToString(t.operandType, depth + 1)}).${t.fieldName.value}` case 'method-call':