Skip to content

Commit

Permalink
Desugar phase; call bounds; return type extractor
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanjermakov committed Aug 4, 2024
1 parent c059f7d commit 3d5a616
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 35 deletions.
1 change: 1 addition & 0 deletions src/ast/statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export type FnDef = BaseAstNode & {
returnType?: Type
static?: boolean
pub: boolean
instance?: TraitDef | ImplDef
}

export const buildFnDef = (node: ParseNode, ctx: Context): FnDef => {
Expand Down
3 changes: 2 additions & 1 deletion src/ast/type.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { BaseAstNode } from '.'
import { ParseNode, filterNonAstNodes } from '../parser'
import { Context } from '../scope'
import { InferredType } from '../typecheck'
import { Hole, buildHole } from './match'
import { Identifier, Name, buildIdentifier, buildName } from './operand'

export type Type = Identifier | FnType | Hole
export type Type = Identifier | FnType | Hole | InferredType

export const buildType = (node: ParseNode, ctx: Context): Type => {
const n = filterNonAstNodes(node)[0]
Expand Down
Empty file added src/phase/setPubType.ts
Empty file.
10 changes: 6 additions & 4 deletions src/phase/sugar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Context, idFromString } from '../scope'
* Desugar phase that runs before name resolution
* Does:
* - populates type of the `self` param
* - sets fnDef.instance
*/
export const desugar1 = (node: AstNode, ctx: Context, parent?: AstNode) => {
switch (node.kind) {
Expand All @@ -18,15 +19,16 @@ export const desugar1 = (node: AstNode, ctx: Context, parent?: AstNode) => {
break
}
case 'fn-def': {
node.params.forEach((p, i) => {
if (parent && (parent.kind === 'impl-def' || parent.kind === 'trait-def')) {
if (parent && (parent.kind === 'impl-def' || parent.kind === 'trait-def')) {
node.instance = parent
node.params.forEach((p, i) => {
if (!p.paramType && i === 0) {
const selfType = idFromString('Self')
selfType.parseNode = p.parseNode
p.paramType = selfType
}
}
})
})
}
break
}
}
Expand Down
69 changes: 46 additions & 23 deletions src/phase/type-bound.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { AstNode } from '../ast'
import { Identifier } from '../ast/operand'
import { Type } from '../ast/type'
import { Context } from '../scope'
import { operatorImplMap } from '../semantic/op'
import { InferredType, makeInferredType } from '../typecheck'
import { InferredType, makeInferredType, makeKnownType } from '../typecheck'
import { boolId, charId, floatId, intId, stringId, unitId } from '../typecheck/type'
import { assert } from '../util/todo'
import { findById } from './name-resolve'

/**
* Set known types of public nodes
*/
export const setPubType = (node: AstNode, ctx: Context) => {
export const setPubType = (node: AstNode, ctx: Context, parent?: AstNode) => {
switch (node.kind) {
case 'module': {
node.block.statements.forEach(s => setPubType(s, ctx))
Expand All @@ -20,24 +21,24 @@ export const setPubType = (node: AstNode, ctx: Context) => {
if (node.pattern.expr.kind !== 'name') break
const def = node.pattern.expr
def.type = makeInferredType()
def.type.known = node.varType
setKnown(def.type, node.varType)
break
}
case 'fn-def': {
node.params.forEach(p => {
p.type = makeInferredType()
if (p.paramType) {
p.type.known = p.paramType
setKnown(p.type, p.paramType)
}
})
node.type = makeInferredType()
node.type!.known = {
setKnown(node.type, {
kind: 'fn-type',
parseNode: node.name.parseNode,
generics: node.generics,
paramTypes: node.params.map(p => p.type?.known ?? { kind: 'hole' }),
paramTypes: node.params.map(p => p.type!),
returnType: node.returnType ?? unitId
}
})
break
}
case 'type-def': {
Expand All @@ -51,23 +52,23 @@ export const setPubType = (node: AstNode, ctx: Context) => {
node.variants.forEach(v => {
v.fieldDefs.forEach(f => {
f.type = makeInferredType()
f.type.known = f.fieldType
setKnown(f.type, f.fieldType)
})
v.type = makeInferredType()
v.type.known = {
setKnown(v.type, {
kind: 'fn-type',
parseNode: node.name.parseNode,
generics: node.generics,
paramTypes: v.fieldDefs.map(f => f.type!.known!),
paramTypes: v.fieldDefs.map(f => f.type!),
returnType: nodeId
}
})
})
break
}
case 'trait-def':
case 'impl-def': {
if (node.kind === 'impl-def' && node.forTrait) break
node.block.statements.forEach(s => setPubType(s, ctx))
node.block.statements.forEach(s => setPubType(s, ctx, node))
break
}
}
Expand Down Expand Up @@ -105,7 +106,7 @@ export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: Inf
node.type ??= makeInferredType()
}
if (node.type && parentBound) {
addBound(node.type, parentBound)
addBounds(node.type, [parentBound])
}
switch (node.kind) {
case 'module': {
Expand All @@ -132,7 +133,7 @@ export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: Inf
case 'param': {
// TODO: handle self
if (node.paramType) return undefined
node.type!.known = node.paramType
setKnown(node.type!, node.paramType)
collectTypeBounds(node.pattern, ctx, node.paramType)
return node.type
}
Expand Down Expand Up @@ -182,7 +183,7 @@ export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: Inf
}
case 'string-interpolated': {
node.tokens.filter(t => typeof t !== 'string').forEach(t => collectTypeBounds(t, ctx))
node.type!.known = stringId
setKnown(node.type!, stringId)
break
}
case 'operand-expr': {
Expand All @@ -200,7 +201,13 @@ export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: Inf
assert(!!methodId)
const methodDef = findById(methodId!, ctx)
assert(!!methodDef)
node.type!.bounds.push(methodDef!.type!)
const op = node.binaryOp
op.type = makeInferredType()
addBounds(op.type, [
methodDef!.type!,
makeKnownType(boundFromCall([node.lOperand.type!, node.rOperand.type!]))
])
node.type = { kind: 'return', type: op.type }
// TODO
break
}
Expand Down Expand Up @@ -247,23 +254,23 @@ export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: Inf
break
}
case 'string-literal': {
node.type!.known = stringId
setKnown(node.type!, stringId)
break
}
case 'char-literal': {
node.type!.known = charId
setKnown(node.type!, charId)
break
}
case 'int-literal': {
node.type!.known = intId
setKnown(node.type!, intId)
break
}
case 'float-literal': {
node.type!.known = floatId
setKnown(node.type!, floatId)
break
}
case 'bool-literal': {
node.type!.known = boolId
setKnown(node.type!, boolId)
break
}
case 'method-call-op': {
Expand Down Expand Up @@ -294,6 +301,22 @@ export const collectTypeBounds = (node: AstNode, ctx: Context, parentBound?: Inf
return node.type
}

const addBound = (type: InferredType, bound: InferredType): void => {
type.bounds.push(bound)
const addBounds = (type: InferredType, bounds: InferredType[]): void => {
if (type.kind === 'inferred') {
type.bounds.push(...bounds)
return
}
assert(false, type.kind)
}

const setKnown = (type: InferredType, known?: Type): void => {
if (type.kind === 'inferred') {
type.known = known
return
}
assert(false, type.kind)
}

const boundFromCall = (args: Type[]): Type => {
return { kind: 'fn-type', generics: [], paramTypes: args, returnType: { kind: 'hole' } }
}
8 changes: 7 additions & 1 deletion src/scope/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ export const idFromString = (str: string, parseNode?: ParseNode): Identifier =>
}

export const inferredTypeToString = (t: InferredType): string => {
const bounds = t.bounds.length > 0 ? `: ${t.bounds.map(inferredTypeToString).join(' + ')}` : ''
if (t.kind === 'return') {
return `{${inferredTypeToString(t.type)}}.ret`
}
const bounds = t.bounds.length > 0 ? `: (${t.bounds.map(inferredTypeToString).join(', ')})` : ''
return `${t.known ? typeToString(t.known) : '_'}${bounds}`
}

Expand All @@ -93,6 +96,9 @@ export const typeToString = (t: Type): string => {
return typeArgs + main
case 'hole':
return '_'
case 'inferred':
case 'return':
return inferredTypeToString(t)
}
}

Expand Down
19 changes: 13 additions & 6 deletions src/typecheck/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { Type } from '../ast/type'

export type InferredType = {
kind: 'inferred'
known?: Type
bounds: InferredType[]
unified?: Type
}
export type InferredType =
| {
kind: 'inferred'
bounds: InferredType[]
known?: Type
unified?: Type
}
| {
kind: 'return'
type: InferredType
}

export const makeInferredType = (bounds: InferredType[] = []): InferredType => ({ kind: 'inferred', bounds })

export const makeKnownType = (known: Type): InferredType => ({ kind: 'inferred', bounds: [], known })

0 comments on commit 3d5a616

Please sign in to comment.