|
|
|
|
@@ -64,7 +64,7 @@ type
|
|
|
|
|
## Forward declarations
|
|
|
|
|
proc isStaticallyConvertible(value: TypedExpr, b: Type): bool
|
|
|
|
|
proc compareWithContext(self: TypeChecker, a: TypedExpr, b: Type): Type
|
|
|
|
|
proc check(self: TypeChecker, node: ASTNode): TypedNode
|
|
|
|
|
proc typecheck(self: TypeChecker, node: ASTNode): TypedNode
|
|
|
|
|
proc toIntrinsic(name: string): Type
|
|
|
|
|
proc done(self: TypeChecker): bool {.inline.}
|
|
|
|
|
proc toRef(self: Type): Type {.inline.}
|
|
|
|
|
@@ -76,7 +76,6 @@ proc unwrapType(self: Type): Type {.inline.}
|
|
|
|
|
proc handleErrorPragma(self: TypeChecker, pragma: Pragma, name: Name)
|
|
|
|
|
proc handlePurePragma(self: TypeChecker, pragma: Pragma, name: Name)
|
|
|
|
|
proc handleMagicPragma(self: TypeChecker, pragma: Pragma, name: Name)
|
|
|
|
|
proc handleUnsafePragma(self: TypeChecker, pragma: Pragma, name: Name)
|
|
|
|
|
proc handleWarnPragma(self: TypeChecker, pragma: Pragma, name: Name)
|
|
|
|
|
proc warning(self: TypeChecker, kind: WarningKind, message: string, name: Name = nil, node: ASTNode = nil)
|
|
|
|
|
proc stringify*(self: TypeChecker, typ: Type, verbose = false): string
|
|
|
|
|
@@ -86,7 +85,6 @@ proc compare(self: TypeChecker, a, b: Type): bool
|
|
|
|
|
proc expression(self: TypeChecker, node: Expression): TypedExpr
|
|
|
|
|
proc declareGenerics(self: TypeChecker, name: Name)
|
|
|
|
|
proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedFunDecl
|
|
|
|
|
proc getTypeDistance(self: TypeChecker, a, b: Type): int
|
|
|
|
|
proc addName(self: TypeChecker, name: Name)
|
|
|
|
|
proc find(self: TypeChecker, name: string, kind: Type = "any".toIntrinsic(), skip: int = 0): Name
|
|
|
|
|
proc ensureBoolean(self: TypeChecker, node: Expression): TypedExpr {.inline.}
|
|
|
|
|
@@ -117,7 +115,6 @@ proc newTypeChecker*: TypeChecker =
|
|
|
|
|
result.pragmas["pure"] = PragmaFunc(kind: Immediate, handler: handlePurePragma)
|
|
|
|
|
result.pragmas["error"] = PragmaFunc(kind: Delayed, handler: handleErrorPragma)
|
|
|
|
|
result.pragmas["warn"] = PragmaFunc(kind: Delayed, handler: handleWarnPragma)
|
|
|
|
|
result.pragmas["unsafe"] = PragmaFunc(kind: Immediate, handler: handleUnsafePragma)
|
|
|
|
|
result.showMismatches = false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -161,10 +158,7 @@ proc handleMagicPragma(self: TypeChecker, pragma: Pragma, name: Name) =
|
|
|
|
|
if pragma.args[0].token.lexeme[1..^2] == "owo":
|
|
|
|
|
# :3
|
|
|
|
|
self.error("Thou hast discovered ze forbidden type. Huzzah! :3", pragma)
|
|
|
|
|
let typ = pragma.args[0].token.lexeme[1..^2].toIntrinsic()
|
|
|
|
|
if typ.isNil():
|
|
|
|
|
self.error("'magic' pragma: wrong argument value", pragma.args[0])
|
|
|
|
|
name.valueType = typ
|
|
|
|
|
name.valueType = pragma.args[0].token.lexeme[1..^2].toIntrinsic()
|
|
|
|
|
elif name.node.kind == NodeKind.funDecl:
|
|
|
|
|
name.valueType.isBuiltin = true
|
|
|
|
|
name.valueType.builtinOp = pragma.args[0].token.lexeme[1..^2]
|
|
|
|
|
@@ -199,13 +193,6 @@ proc handlePurePragma(self: TypeChecker, pragma: Pragma, name: Name) =
|
|
|
|
|
discard # TODO
|
|
|
|
|
else:
|
|
|
|
|
self.error("'pure' pragma is not valid in this context")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc handleUnsafePragma(self: TypeChecker, pragma: Pragma, name: Name) =
|
|
|
|
|
## Handles the "unsafe" pragma
|
|
|
|
|
if name.node.kind notin [NodeKind.funDecl, NodeKind.lambdaExpr]:
|
|
|
|
|
self.error("'unsafe' pragma is not valid in this context")
|
|
|
|
|
name.valueType.unsafe = true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc warning(self: TypeChecker, kind: WarningKind, message: string, name: Name = nil, node: ASTNode = nil) =
|
|
|
|
|
@@ -254,9 +241,9 @@ proc wrapType(self: Type): Type {.inline.} =
|
|
|
|
|
## Wraps a type in a typevar
|
|
|
|
|
case self.kind:
|
|
|
|
|
of Union:
|
|
|
|
|
result = Type(kind: Union, types: @[], displayName: self.displayName)
|
|
|
|
|
for typ in self.types:
|
|
|
|
|
result.types.add((match: typ.match, kind: typ.kind.wrapType(), value: typ.value))
|
|
|
|
|
result = Type(kind: Union, constraints: @[], displayName: self.displayName)
|
|
|
|
|
for typ in self.constraints:
|
|
|
|
|
result.constraints.add((match: typ.match, kind: typ.kind.wrapType(), value: typ.value))
|
|
|
|
|
else:
|
|
|
|
|
result = Type(kind: Typevar, wrapped: self)
|
|
|
|
|
|
|
|
|
|
@@ -268,9 +255,9 @@ proc unwrapType(self: Type): Type {.inline.} =
|
|
|
|
|
of Typevar:
|
|
|
|
|
result = self.wrapped
|
|
|
|
|
of Union:
|
|
|
|
|
result = Type(kind: Union, types: @[], displayName: self.displayName)
|
|
|
|
|
for typ in self.types:
|
|
|
|
|
result.types.add((match: typ.match, kind: typ.kind.unwrapType(), value: typ.value))
|
|
|
|
|
result = Type(kind: Union, constraints: @[], displayName: self.displayName)
|
|
|
|
|
for typ in self.constraints:
|
|
|
|
|
result.constraints.add((match: typ.match, kind: typ.kind.unwrapType(), value: typ.value))
|
|
|
|
|
else:
|
|
|
|
|
result = self
|
|
|
|
|
|
|
|
|
|
@@ -302,53 +289,51 @@ proc toIntrinsic(name: string): Type =
|
|
|
|
|
## type
|
|
|
|
|
case name:
|
|
|
|
|
of "typevar":
|
|
|
|
|
return Type(kind: Typevar, wrapped: "any".toIntrinsic(), intrinsic: true)
|
|
|
|
|
return Type(kind: Typevar, wrapped: "any".toIntrinsic())
|
|
|
|
|
of "any":
|
|
|
|
|
return Type(kind: Any, intrinsic: true)
|
|
|
|
|
of "auto":
|
|
|
|
|
return Type(kind: Auto, intrinsic: true)
|
|
|
|
|
return Type(kind: Any)
|
|
|
|
|
of "int64", "i64":
|
|
|
|
|
return Type(kind: Integer, size: LongLong, signed: true, intrinsic: true)
|
|
|
|
|
return Type(kind: Integer, size: LongLong, signed: true)
|
|
|
|
|
of "uint64", "u64":
|
|
|
|
|
return Type(kind: Integer, size: LongLong, signed: false, intrinsic: true)
|
|
|
|
|
return Type(kind: Integer, size: LongLong, signed: false)
|
|
|
|
|
of "int32", "i32":
|
|
|
|
|
return Type(kind: Integer, size: Long, signed: true, intrinsic: true)
|
|
|
|
|
return Type(kind: Integer, size: Long, signed: true)
|
|
|
|
|
of "uint32", "u32":
|
|
|
|
|
return Type(kind: Integer, size: Long, signed: false, intrinsic: true)
|
|
|
|
|
return Type(kind: Integer, size: Long, signed: false)
|
|
|
|
|
of "int16", "i16":
|
|
|
|
|
return Type(kind: Integer, size: Short, signed: true, intrinsic: true)
|
|
|
|
|
return Type(kind: Integer, size: Short, signed: true)
|
|
|
|
|
of "uint16", "u16":
|
|
|
|
|
return Type(kind: Integer, size: Short, signed: false, intrinsic: true)
|
|
|
|
|
return Type(kind: Integer, size: Short, signed: false)
|
|
|
|
|
of "int8", "i8":
|
|
|
|
|
return Type(kind: Integer, size: Tiny, signed: true, intrinsic: true)
|
|
|
|
|
return Type(kind: Integer, size: Tiny, signed: true)
|
|
|
|
|
of "uint8", "u8":
|
|
|
|
|
return Type(kind: Integer, size: Tiny, signed: false, intrinsic: true)
|
|
|
|
|
return Type(kind: Integer, size: Tiny, signed: false)
|
|
|
|
|
of "float", "float64", "f64":
|
|
|
|
|
return Type(kind: Float, width: Full, intrinsic: true)
|
|
|
|
|
return Type(kind: Float, width: Full)
|
|
|
|
|
of "float32", "f32":
|
|
|
|
|
return Type(kind: Float, width: Half, intrinsic: true)
|
|
|
|
|
return Type(kind: Float, width: Half)
|
|
|
|
|
of "byte":
|
|
|
|
|
return Type(kind: Byte, intrinsic: true)
|
|
|
|
|
return Type(kind: Byte)
|
|
|
|
|
of "char":
|
|
|
|
|
return Type(kind: Char, intrinsic: true)
|
|
|
|
|
return Type(kind: Char)
|
|
|
|
|
of "NaN":
|
|
|
|
|
return Type(kind: TypeKind.Nan, intrinsic: true)
|
|
|
|
|
return Type(kind: TypeKind.Nan)
|
|
|
|
|
of "Inf":
|
|
|
|
|
return Type(kind: Infinity, positive: true, intrinsic: true)
|
|
|
|
|
return Type(kind: Infinity, positive: true)
|
|
|
|
|
of "NegInf":
|
|
|
|
|
return Type(kind: Infinity, intrinsic: true)
|
|
|
|
|
return Type(kind: Infinity)
|
|
|
|
|
of "bool":
|
|
|
|
|
return Type(kind: Boolean, intrinsic: true)
|
|
|
|
|
return Type(kind: Boolean)
|
|
|
|
|
of "string":
|
|
|
|
|
return Type(kind: String, intrinsic: true)
|
|
|
|
|
return Type(kind: String)
|
|
|
|
|
of "pointer":
|
|
|
|
|
return Type(kind: Pointer, value: "any".toIntrinsic(), intrinsic: true)
|
|
|
|
|
return Type(kind: Pointer, value: "any".toIntrinsic())
|
|
|
|
|
of "lent":
|
|
|
|
|
return Type(kind: TypeKind.Lent, value: "any".toIntrinsic(), intrinsic: true)
|
|
|
|
|
return Type(kind: TypeKind.Lent, value: "any".toIntrinsic())
|
|
|
|
|
of "const":
|
|
|
|
|
return Type(kind: TypeKind.Const, value: "any".toIntrinsic(), intrinsic: true)
|
|
|
|
|
return Type(kind: TypeKind.Const, value: "any".toIntrinsic())
|
|
|
|
|
of "ref":
|
|
|
|
|
return Type(kind: Reference, value: "any".toIntrinsic(), intrinsic: true)
|
|
|
|
|
return Type(kind: Reference, value: "any".toIntrinsic())
|
|
|
|
|
else:
|
|
|
|
|
raise newException(ValueError, &"invalid intrinsic '{name}'")
|
|
|
|
|
|
|
|
|
|
@@ -442,7 +427,7 @@ proc isAny(typ: Type): bool =
|
|
|
|
|
of Any:
|
|
|
|
|
return true
|
|
|
|
|
of Union:
|
|
|
|
|
for condition in typ.types:
|
|
|
|
|
for condition in typ.constraints:
|
|
|
|
|
if condition.kind.isAny():
|
|
|
|
|
return true
|
|
|
|
|
of Typevar:
|
|
|
|
|
@@ -531,8 +516,7 @@ proc compare(self: TypeChecker, a, b: Type): bool =
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
of Boolean, Infinity, Any,
|
|
|
|
|
Auto, Char, Byte, String:
|
|
|
|
|
of Boolean, Infinity, Any, Char, Byte, String:
|
|
|
|
|
return true
|
|
|
|
|
of Integer:
|
|
|
|
|
return a.size == b.size and a.signed == b.signed
|
|
|
|
|
@@ -540,10 +524,10 @@ proc compare(self: TypeChecker, a, b: Type): bool =
|
|
|
|
|
return a.width == b.width
|
|
|
|
|
of TypeKind.Lent, Reference, Pointer, TypeKind.Const:
|
|
|
|
|
return self.compare(a.value, b.value)
|
|
|
|
|
of Union:
|
|
|
|
|
if a.types.len() > b.types.len():
|
|
|
|
|
return self.isSubsetOf(b.types, a.types)
|
|
|
|
|
return self.isSubsetOf(a.types, b.types)
|
|
|
|
|
of Union, Generic:
|
|
|
|
|
if a.constraints.len() > b.constraints.len():
|
|
|
|
|
return self.isSubsetOf(b.constraints, a.constraints)
|
|
|
|
|
return self.isSubsetOf(a.constraints, b.constraints)
|
|
|
|
|
of Function:
|
|
|
|
|
# TODO
|
|
|
|
|
return false
|
|
|
|
|
@@ -551,9 +535,9 @@ proc compare(self: TypeChecker, a, b: Type): bool =
|
|
|
|
|
# TODO
|
|
|
|
|
return false
|
|
|
|
|
if a.kind == Union:
|
|
|
|
|
return self.matchUnion(b, a.types)
|
|
|
|
|
return self.matchUnion(b, a.constraints)
|
|
|
|
|
if b.kind == Union:
|
|
|
|
|
return self.matchUnion(a, b.types)
|
|
|
|
|
return self.matchUnion(a, b.constraints)
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -734,8 +718,7 @@ proc stringify*(self: TypeChecker, typ: Type, verbose: bool = false): string =
|
|
|
|
|
if typ.isNil():
|
|
|
|
|
return "void"
|
|
|
|
|
case typ.kind:
|
|
|
|
|
of Char, Byte, String, TypeKind.Nan,
|
|
|
|
|
Auto, Any:
|
|
|
|
|
of Char, Byte, String, TypeKind.Nan, Any:
|
|
|
|
|
result &= ($typ.kind).toLowerAscii()
|
|
|
|
|
of Structure:
|
|
|
|
|
result &= typ.name
|
|
|
|
|
@@ -743,7 +726,7 @@ proc stringify*(self: TypeChecker, typ: Type, verbose: bool = false): string =
|
|
|
|
|
result &= "["
|
|
|
|
|
var i = 0
|
|
|
|
|
for gen in typ.generics.keys():
|
|
|
|
|
result &= &"{gen}: {self.stringify(typ.generics[gen])}"
|
|
|
|
|
result &= &"{gen}: {self.stringify(typ.generics[gen], true)}"
|
|
|
|
|
if i < typ.generics.len() - 1:
|
|
|
|
|
result &= ", "
|
|
|
|
|
inc(i)
|
|
|
|
|
@@ -814,15 +797,12 @@ proc stringify*(self: TypeChecker, typ: Type, verbose: bool = false): string =
|
|
|
|
|
of Generic, Union:
|
|
|
|
|
if typ.displayName.len() > 0 and not verbose:
|
|
|
|
|
return typ.displayName
|
|
|
|
|
for i, condition in typ.types:
|
|
|
|
|
for i, condition in typ.constraints:
|
|
|
|
|
if i > 0:
|
|
|
|
|
result &= " | "
|
|
|
|
|
if not condition.match:
|
|
|
|
|
result &= "~"
|
|
|
|
|
if typ.kind == Generic:
|
|
|
|
|
result &= self.stringify(condition.kind.unwrapType())
|
|
|
|
|
else:
|
|
|
|
|
result &= self.stringify(condition.kind)
|
|
|
|
|
result &= self.stringify(condition.kind)
|
|
|
|
|
of Typevar:
|
|
|
|
|
result &= &"typevar[{self.stringify(typ.wrapped)}]"
|
|
|
|
|
else:
|
|
|
|
|
@@ -884,11 +864,29 @@ proc check(self: TypeChecker, term, expected: Type, node: ASTNode = nil): Type {
|
|
|
|
|
## Raises an error if appropriate and returns the typed expression
|
|
|
|
|
## otherwise. The node is passed in to error() in case of a failure
|
|
|
|
|
## for more precise error reporting
|
|
|
|
|
if not self.compare(term, expected):
|
|
|
|
|
self.error(&"expecting an expression of type {self.stringify(expected, true)}, got {self.stringify(term, true)} instead", node)
|
|
|
|
|
result = term
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc check(self: TypeChecker, term, expected: TypedExpr, node: ASTNode = nil): TypedExpr {.inline, discardable.} =
|
|
|
|
|
## Checks the type of the given expression against a known one.
|
|
|
|
|
## Raises an error if appropriate and returns the typed expression
|
|
|
|
|
## otherwise. The node is passed in to error() in case of a failure
|
|
|
|
|
## for more precise error reporting
|
|
|
|
|
if not self.compare(term.kind, expected.kind):
|
|
|
|
|
self.error(&"expecting an expression of type {self.stringify(expected, true)}, got {self.stringify(term, true)} instead", node)
|
|
|
|
|
result = term
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc check(self: TypeChecker, term: TypedExpr, expected: Type, node: ASTNode = nil): TypedExpr {.inline, discardable.} =
|
|
|
|
|
## Checks the type of the given expression against a known one.
|
|
|
|
|
## Raises an error if appropriate and returns the typed expression
|
|
|
|
|
## otherwise. The node is passed in to error() in case of a failure
|
|
|
|
|
## for more precise error reporting
|
|
|
|
|
if not self.compare(term.kind, expected):
|
|
|
|
|
self.error(&"expecting an expression of type {self.stringify(expected, true)}, got {self.stringify(term, true)} instead", node)
|
|
|
|
|
result = term
|
|
|
|
|
if not self.compare(result, expected):
|
|
|
|
|
self.error(&"expecting an expression of type {self.stringify(expected, true)}, got {self.stringify(result, true)} instead", node)
|
|
|
|
|
if result.isAny() and not expected.isAny():
|
|
|
|
|
self.error("any is not a valid type in this context", node)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc checkWithContext(self: TypeChecker, term: TypedExpr, expected: Type, node: ASTNode = nil): Type {.inline, discardable.} =
|
|
|
|
|
@@ -897,113 +895,19 @@ proc checkWithContext(self: TypeChecker, term: TypedExpr, expected: Type, node:
|
|
|
|
|
result = self.compareWithContext(term, expected)
|
|
|
|
|
if result.isNil():
|
|
|
|
|
self.error(&"expecting an expression of type {self.stringify(expected, true)}, got {self.stringify(term, true)} instead", node)
|
|
|
|
|
if result.isAny() and not expected.isAny():
|
|
|
|
|
self.error("any is not a valid type in this context", node)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc check(self: TypeChecker, term: Expression, expected: Type): TypedExpr {.inline, discardable.} =
|
|
|
|
|
proc check(self: TypeChecker, term: Expression, expected: Type, node: ASTNode = nil): TypedExpr {.inline, discardable.} =
|
|
|
|
|
## Checks the type of the given expression against a known one.
|
|
|
|
|
## Raises an error if appropriate and returns the typed expression
|
|
|
|
|
## otherwise
|
|
|
|
|
result = self.inferOrError(term)
|
|
|
|
|
self.check(result.kind, expected, term)
|
|
|
|
|
result = self.check(self.inferOrError(term), expected, node)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc getTypeDistance(self: TypeChecker, a, b: Type): int =
|
|
|
|
|
## Gets the type distance of two Peon types. Assumes
|
|
|
|
|
## a and b are already compatible (i.e. compare(a, b)
|
|
|
|
|
## returns true). For more info, check out self.match()
|
|
|
|
|
if a.kind != Structure or b.kind != Structure:
|
|
|
|
|
# TODO: Test
|
|
|
|
|
return 0
|
|
|
|
|
var parent = b.parent
|
|
|
|
|
result = 0
|
|
|
|
|
# We already know that the inheritance
|
|
|
|
|
# chain is correct (i.e. that a is at the
|
|
|
|
|
# root of it) because we assume a and b are
|
|
|
|
|
# already compatible, so we really just need
|
|
|
|
|
# to walk the tree backwards and keep track of
|
|
|
|
|
# how many times we go up one node
|
|
|
|
|
while not parent.isNil():
|
|
|
|
|
# Juuust to be sure
|
|
|
|
|
when defined(debug):
|
|
|
|
|
if parent.parent.isNil():
|
|
|
|
|
assert parent.parent == a
|
|
|
|
|
inc(result)
|
|
|
|
|
parent = parent.parent
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc calcTypeDistance(self: TypeChecker, typ: Type, sig: TypeSignature): int =
|
|
|
|
|
## Computes the cumulative type distance between
|
|
|
|
|
## the given type and the given type signature. It's
|
|
|
|
|
## basically just adding up the type distances of each
|
|
|
|
|
## element in the type/signature to end up with a single
|
|
|
|
|
## value to represent how precisely a given type matches
|
|
|
|
|
## the given signature. Note that this function assumes
|
|
|
|
|
## that the types are already compatible
|
|
|
|
|
case typ.kind:
|
|
|
|
|
of TypeKind.Function:
|
|
|
|
|
for (argA, argB) in zip(typ.signature, sig):
|
|
|
|
|
result += self.getTypeDistance(argA.kind, argB.kind)
|
|
|
|
|
of TypeKind.Structure:
|
|
|
|
|
self.error("not implemented")
|
|
|
|
|
else:
|
|
|
|
|
self.error(&"cannot compute type distance for object of type {self.stringify(typ)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc checkTypeSignature(self: TypeChecker, typ: Type, sig: TypeSignature): bool =
|
|
|
|
|
## Helper to check type signatures against a known
|
|
|
|
|
## one. Returns true if the given type matches the
|
|
|
|
|
## given type signature
|
|
|
|
|
case typ.kind:
|
|
|
|
|
of TypeKind.Function:
|
|
|
|
|
if typ.signature.len() < sig.len():
|
|
|
|
|
# If the function has less arguments than
|
|
|
|
|
# we have in our signature, then we surely
|
|
|
|
|
# can't match to it. This is different from
|
|
|
|
|
# the code in self.compare() (which checks that
|
|
|
|
|
# both signatures have the same length) because
|
|
|
|
|
# of how we handle default arguments; This addresses
|
|
|
|
|
# a problem that arises that when we have a function
|
|
|
|
|
# that looks like (a, b, c = someDefault) -> z and we're
|
|
|
|
|
# looking for a signature (a, b) -> y: if we enforced that
|
|
|
|
|
# the length of the signatures must be the same, we'd consider
|
|
|
|
|
# that function to not be a good match, even though it is
|
|
|
|
|
# because the third argument has a default value
|
|
|
|
|
return false
|
|
|
|
|
# We construct a new signature, without the default arguments
|
|
|
|
|
var args: TypeSignature = @[]
|
|
|
|
|
for argument in typ.signature:
|
|
|
|
|
if argument.default.isNil():
|
|
|
|
|
args.add(argument)
|
|
|
|
|
else:
|
|
|
|
|
break
|
|
|
|
|
if args.len() != sig.len():
|
|
|
|
|
return false
|
|
|
|
|
for (argA, argB) in zip(args, sig):
|
|
|
|
|
if not self.compare(argA.kind, argB.kind):
|
|
|
|
|
return false
|
|
|
|
|
if argA.name == "" or argB.name == "":
|
|
|
|
|
continue
|
|
|
|
|
elif argA.name != argB.name:
|
|
|
|
|
return false
|
|
|
|
|
return true
|
|
|
|
|
of TypeKind.Structure:
|
|
|
|
|
if sig.len() != typ.fields.len():
|
|
|
|
|
# For now, we require that all fields of an object
|
|
|
|
|
# be explicitly initialized
|
|
|
|
|
return false
|
|
|
|
|
var fields: seq[Type] = @[]
|
|
|
|
|
var names: HashSet[string]
|
|
|
|
|
for fieldName in typ.fields.keys():
|
|
|
|
|
fields.add(typ.fields[fieldName])
|
|
|
|
|
else:
|
|
|
|
|
self.error(&"cannot check type signature for object of type {self.stringify(typ)}")
|
|
|
|
|
|
|
|
|
|
func toCapability(typ: Type, value: TypedExpr = nil): TypeCapabilities =
|
|
|
|
|
case typ.kind:
|
|
|
|
|
of Union, Generic:
|
|
|
|
|
for unionType in typ.types:
|
|
|
|
|
for unionType in typ.constraints:
|
|
|
|
|
for element in unionType.kind.toCapability(unionType.value):
|
|
|
|
|
result.add(element)
|
|
|
|
|
else:
|
|
|
|
|
@@ -1037,6 +941,7 @@ proc isCapable(self: TypeChecker, name: Name, sig: TypeSignature): bool =
|
|
|
|
|
if self.capabilities[functionName].len() == 0:
|
|
|
|
|
return false
|
|
|
|
|
# Find the right capability
|
|
|
|
|
# TODO: Probably a good idea to cache this
|
|
|
|
|
let candidateCapability = sig.toCapability()
|
|
|
|
|
var superCapability: TypeCapabilities
|
|
|
|
|
for capability in self.capabilities[functionName]:
|
|
|
|
|
@@ -1044,13 +949,9 @@ proc isCapable(self: TypeChecker, name: Name, sig: TypeSignature): bool =
|
|
|
|
|
result = self.isSubsetOf(candidateCapability, superCapability)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc matchCapability(self: TypeChecker, name: string, sig: TypeSignature, node: ASTNode = nil): Name =
|
|
|
|
|
var
|
|
|
|
|
candidates: seq[Name] = @[]
|
|
|
|
|
matches: seq[Name] = @[]
|
|
|
|
|
distances: seq[int] = @[]
|
|
|
|
|
dst: int = 0
|
|
|
|
|
minDst: int = dst
|
|
|
|
|
proc match(self: TypeChecker, name: string, sig: TypeSignature, node: ASTNode = nil): Name =
|
|
|
|
|
## Attempts to find a suitable named implementation with the given type signature
|
|
|
|
|
var candidates: seq[Name] = @[]
|
|
|
|
|
# Find all matching implementations and record their
|
|
|
|
|
# type distance relative to our input type signature,
|
|
|
|
|
# as well as the smallest one found so far
|
|
|
|
|
@@ -1058,58 +959,8 @@ proc matchCapability(self: TypeChecker, name: string, sig: TypeSignature, node:
|
|
|
|
|
# TODO: Handle default arguments
|
|
|
|
|
if self.isCapable(candidate, sig):
|
|
|
|
|
candidates.add(candidate)
|
|
|
|
|
dst = self.calcTypeDistance(candidate.valueType, sig)
|
|
|
|
|
if dst < minDst:
|
|
|
|
|
minDst = dst
|
|
|
|
|
distances.add(dst)
|
|
|
|
|
# When we look for an object to instantiate with a given type signature, we
|
|
|
|
|
# incrementally build a list of potential matches: if the resulting list has
|
|
|
|
|
# length zero, that means no match has been found and we should report the error
|
|
|
|
|
# to the user; A similarly trivial case is that of a list of length one, except of
|
|
|
|
|
# course it's not an error (it just means we have found the only match). A more
|
|
|
|
|
# interesting case is that of multiple candidate matches: the simple answer would
|
|
|
|
|
# be to just throw an exception warning the user of the ambiguity, except that sometimes
|
|
|
|
|
# that backfires hard. A practical example is going to be useful: say you have a function
|
|
|
|
|
# sum that takes in two integers and returns their sum. In peon, you would write something
|
|
|
|
|
# like this:
|
|
|
|
|
#
|
|
|
|
|
# fn sum(a, b: int): int {
|
|
|
|
|
# return a + b;
|
|
|
|
|
# }
|
|
|
|
|
#
|
|
|
|
|
# Let's now say that I overload this for some arbitrary subtype of int, MyInt
|
|
|
|
|
#
|
|
|
|
|
# fn sum(a, b: MyInt): MyInt {
|
|
|
|
|
# # code here
|
|
|
|
|
# }
|
|
|
|
|
#
|
|
|
|
|
# If I now were to call sum with two integers, that would work because the compiler
|
|
|
|
|
# can only find one function that matches the type signature (1, 2) for the name sum;
|
|
|
|
|
# If you attempted to call sum with arguments of type MyInt, however, the compiler would
|
|
|
|
|
# find that both functions taking int and MyInt are valid matches: this is because in peon
|
|
|
|
|
# (and in many other languages) you can always treat a given subtype like an instance of its
|
|
|
|
|
# supertype (potentially losing information, of course). The problem lies in our comparison
|
|
|
|
|
# function, which has no way of judging how "good" a given match is: in our example above,
|
|
|
|
|
# we can easily see that the overloaded implementation is the one that most closely matches
|
|
|
|
|
# the type signature we're looking for (since (MyInt, MyInt) -> MyInt is more precise than
|
|
|
|
|
# (int, int) -> int). So what we do is basically gauge how good each potential match is by
|
|
|
|
|
# computing a metric I call "type distance". Before explaining what type distance is, I'd like
|
|
|
|
|
# to point out that it is a strictly relative metric: it measures how closely a given type
|
|
|
|
|
# signature matches the one we're looking for, but only in relation to all of the other potentially
|
|
|
|
|
# compatible matches for a given lookup. This means that comparing type distances between types that
|
|
|
|
|
# are not compatible just doesn't make sense (you could say they're "infinitely distant"). Of course,
|
|
|
|
|
# should we encounter more than one match with the same type distance from our signature that would
|
|
|
|
|
# still be an ambiguity error, but at least the compiler isn't completely clueless now. The way type
|
|
|
|
|
# distance is measured is quite simple: it's simply the relative distance between the two types in their
|
|
|
|
|
# inheritance tree, hence a value of 0 indicates a perfect match (i.e. they're the EXACT same type, structurally
|
|
|
|
|
# speaking at least), while larger values indicate "less precise" ones (you could say they're further apart)
|
|
|
|
|
for i, candidate in candidates:
|
|
|
|
|
# Grab all the matches with the smallest type distance and
|
|
|
|
|
# matching type capability
|
|
|
|
|
if distances[i] == minDst:
|
|
|
|
|
matches.add(candidate)
|
|
|
|
|
|
|
|
|
|
case matches.len():
|
|
|
|
|
case candidates.len():
|
|
|
|
|
of 0:
|
|
|
|
|
# No matches
|
|
|
|
|
let names = self.findAll(name)
|
|
|
|
|
@@ -1138,34 +989,27 @@ proc matchCapability(self: TypeChecker, name: string, sig: TypeSignature, node:
|
|
|
|
|
else:
|
|
|
|
|
msg &= &": name is not defined"
|
|
|
|
|
self.error(msg, node)
|
|
|
|
|
of 1:
|
|
|
|
|
result = candidates[0]
|
|
|
|
|
else:
|
|
|
|
|
# We found only one matching function: easy!
|
|
|
|
|
result = matches[0].deepCopy()
|
|
|
|
|
result.valueType.signature = sig
|
|
|
|
|
# Extra checks
|
|
|
|
|
case result.valueType.kind:
|
|
|
|
|
of Function:
|
|
|
|
|
for (a, b) in zip(result.valueType.signature, sig):
|
|
|
|
|
if not a.kind.isAny() and b.kind.isAny():
|
|
|
|
|
self.error("any is not a valid type in this context", node)
|
|
|
|
|
of Structure:
|
|
|
|
|
for (a, b) in zip(result.valueType.fields.values().toSeq(), sig):
|
|
|
|
|
if not a.isAny() and b.kind.isAny():
|
|
|
|
|
self.error("any is not a valid type in this context", node)
|
|
|
|
|
else:
|
|
|
|
|
# TODO: Enums
|
|
|
|
|
discard
|
|
|
|
|
# Ambiguity detected
|
|
|
|
|
var msg = &"multiple matches found for '{name}'"
|
|
|
|
|
if self.showMismatches:
|
|
|
|
|
msg &= ":"
|
|
|
|
|
for fn in reversed(candidates):
|
|
|
|
|
msg &= &"\n- in {relativePath(fn.file, getCurrentDir())}, line {fn.line} of type {self.stringify(fn.valueType)}"
|
|
|
|
|
else:
|
|
|
|
|
msg &= " (compile with --showMismatches for more details)"
|
|
|
|
|
self.error(msg, node)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc expandTypeConstraints(self: TypeChecker, condition: Expression, list: var TypeCapabilities, accept: bool = true) =
|
|
|
|
|
## Recursively unpacks a type constraint
|
|
|
|
|
case condition.kind:
|
|
|
|
|
of identExpr, genericExpr:
|
|
|
|
|
var inferred = self.ensureType(condition)
|
|
|
|
|
var typ = inferred.kind
|
|
|
|
|
let inferred = self.ensureType(condition)
|
|
|
|
|
let typ = inferred.kind
|
|
|
|
|
|
|
|
|
|
if not typ.isAny() and self.compare(typ, "auto".toIntrinsic()):
|
|
|
|
|
self.error("automatic types cannot be used within type constraints", condition)
|
|
|
|
|
list.add((accept, typ.unwrapType(), inferred))
|
|
|
|
|
of binaryExpr:
|
|
|
|
|
let condition = BinaryExpr(condition)
|
|
|
|
|
@@ -1242,13 +1086,15 @@ proc addName(self: TypeChecker, name: Name) =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc recordCapabilities(self: TypeChecker, function: Name) =
|
|
|
|
|
## Records type capabilities to be able to match against them
|
|
|
|
|
## later
|
|
|
|
|
if not self.capabilities.hasKey(function.ident.token.lexeme):
|
|
|
|
|
self.capabilities[function.ident.token.lexeme] = @[]
|
|
|
|
|
var newCapability: TypeCapabilities
|
|
|
|
|
for parameter in function.valueType.signature:
|
|
|
|
|
case parameter.kind.kind:
|
|
|
|
|
of Generic, Union:
|
|
|
|
|
for constraint in parameter.kind.types:
|
|
|
|
|
for constraint in parameter.kind.constraints:
|
|
|
|
|
newCapability.add((constraint.match, constraint.kind.unwrapType(), constraint.value))
|
|
|
|
|
else:
|
|
|
|
|
newCapability.add((true, parameter.kind.unwrapType(), parameter.default))
|
|
|
|
|
@@ -1337,31 +1183,56 @@ proc unary(self: TypeChecker, node: UnaryExpr): TypedUnaryExpr =
|
|
|
|
|
## Typechecks unary expressions
|
|
|
|
|
var
|
|
|
|
|
default: TypedExpr
|
|
|
|
|
typeOfA = self.infer(node.a)
|
|
|
|
|
typeOfA = self.inferOrError(node.a)
|
|
|
|
|
let fn = Type(kind: Function, returnType: Type(kind: Any), signature: @[("", typeOfA.kind, default)])
|
|
|
|
|
let name = self.matchCapability(node.token.lexeme, fn.signature, node)
|
|
|
|
|
result = newTypedUnaryExpr(node, name.valueType, typeOfA)
|
|
|
|
|
let name = self.match(node.token.lexeme, fn.signature, node)
|
|
|
|
|
result = newTypedUnaryExpr(node, name.valueType.returnType, typeOfA)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc binary(self: TypeChecker, node: BinaryExpr): TypedBinaryExpr =
|
|
|
|
|
## Typechecks binary expressions
|
|
|
|
|
var
|
|
|
|
|
default: TypedExpr
|
|
|
|
|
typeOfA = self.infer(node.a)
|
|
|
|
|
typeOfB = self.infer(node.b)
|
|
|
|
|
typeOfA = self.inferOrError(node.a)
|
|
|
|
|
typeOfB = self.inferOrError(node.b)
|
|
|
|
|
let fn = Type(kind: Function,
|
|
|
|
|
returnType: Type(kind: Any),
|
|
|
|
|
signature: @[("", typeOfA.kind, default), ("", typeOfB.kind, default)])
|
|
|
|
|
let name = self.matchCapability(node.token.lexeme, fn.signature, node)
|
|
|
|
|
let name = self.match(node.token.lexeme, fn.signature, node)
|
|
|
|
|
result = newTypedBinaryExpr(node, name.valueType, typeOfA, typeOfB)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc isTypeSet(self: TypeChecker, typ: Type): bool =
|
|
|
|
|
## Returns whether the given type represents
|
|
|
|
|
## a set of types
|
|
|
|
|
case typ.kind:
|
|
|
|
|
of Typevar:
|
|
|
|
|
return true
|
|
|
|
|
of Union, Generic:
|
|
|
|
|
for t in typ.constraints:
|
|
|
|
|
if not self.isTypeSet(t.kind):
|
|
|
|
|
return false
|
|
|
|
|
else:
|
|
|
|
|
return false
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc genericExpr(self: TypeChecker, node: GenericExpr): TypedExpr =
|
|
|
|
|
## Typechecks generic instantiation
|
|
|
|
|
var args: seq[TypedExpr] = @[]
|
|
|
|
|
for arg in node.args:
|
|
|
|
|
args.add(self.inferOrError(arg))
|
|
|
|
|
result = newTypedExpr(node, self.inferOrError(node.ident).kind)
|
|
|
|
|
let typExpr = self.ensureType(node.ident)
|
|
|
|
|
let typ = typExpr.kind.unwrapType()
|
|
|
|
|
result = newTypedGenericExpr(node, typ, args)
|
|
|
|
|
if result.kind.kind == Typevar:
|
|
|
|
|
# Typevars are a special case
|
|
|
|
|
if len(args) > 1:
|
|
|
|
|
self.error(&"too many arguments in generic instantiation (expected 1, got {len(args)} instead)", node)
|
|
|
|
|
if not self.isTypeSet(args[0].kind):
|
|
|
|
|
self.error(&"typevar instantiation only expects types, not values (got expression of type {self.stringify(args[0].kind)})", args[0].node)
|
|
|
|
|
result.kind = result.kind.deepCopy()
|
|
|
|
|
result.kind.wrapped = args[0].kind
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc call(self: TypeChecker, node: CallExpr): TypedExpr =
|
|
|
|
|
@@ -1370,19 +1241,18 @@ proc call(self: TypeChecker, node: CallExpr): TypedExpr =
|
|
|
|
|
var args: TypeSignature = @[]
|
|
|
|
|
var argExpr: seq[TypedExpr] = @[]
|
|
|
|
|
var default: TypedExpr
|
|
|
|
|
var kind: Type
|
|
|
|
|
for i, argument in node.arguments.positionals:
|
|
|
|
|
kind = self.inferOrError(argument).kind
|
|
|
|
|
args.add(("", kind, default))
|
|
|
|
|
argExpr.add(self.expression(argument))
|
|
|
|
|
let typedExpr = self.expression(argument)
|
|
|
|
|
args.add(("", typedExpr.kind, default))
|
|
|
|
|
argExpr.add(typedExpr)
|
|
|
|
|
for i, argument in node.arguments.keyword:
|
|
|
|
|
kind = self.infer(argument.value).kind
|
|
|
|
|
args.add((argument.name.token.lexeme, kind, default))
|
|
|
|
|
argExpr.add(self.expression(argument.value))
|
|
|
|
|
let typedExpr = self.expression(argument.value)
|
|
|
|
|
args.add((argument.name.token.lexeme, typedExpr.kind, default))
|
|
|
|
|
argExpr.add(typedExpr)
|
|
|
|
|
case node.callee.kind:
|
|
|
|
|
of NodeKind.identExpr:
|
|
|
|
|
# Calls like hi()
|
|
|
|
|
var impl = self.matchCapability(IdentExpr(node.callee).name.lexeme, args, node)
|
|
|
|
|
var impl = self.match(IdentExpr(node.callee).name.lexeme, args, node)
|
|
|
|
|
self.dispatchDelayedPragmas(impl)
|
|
|
|
|
case impl.valueType.kind:
|
|
|
|
|
of Structure:
|
|
|
|
|
@@ -1396,8 +1266,7 @@ proc call(self: TypeChecker, node: CallExpr): TypedExpr =
|
|
|
|
|
self.error("not implemented")
|
|
|
|
|
of NodeKind.callExpr:
|
|
|
|
|
# Calling a call expression, like hello()()
|
|
|
|
|
# TODO
|
|
|
|
|
#[
|
|
|
|
|
# TODO: is this right?
|
|
|
|
|
var node: Expression = node
|
|
|
|
|
var all: seq[CallExpr] = @[]
|
|
|
|
|
# Since there can be as many consecutive calls as
|
|
|
|
|
@@ -1410,23 +1279,17 @@ proc call(self: TypeChecker, node: CallExpr): TypedExpr =
|
|
|
|
|
# one and work our way to the innermost call
|
|
|
|
|
for exp in all:
|
|
|
|
|
result = self.call(exp)
|
|
|
|
|
]#
|
|
|
|
|
self.error("not implemented")
|
|
|
|
|
of NodeKind.getterExpr:
|
|
|
|
|
# Calling a.b()
|
|
|
|
|
# TODO
|
|
|
|
|
let node = GetterExpr(node.callee)
|
|
|
|
|
self.error("not implemented")
|
|
|
|
|
of NodeKind.lambdaExpr:
|
|
|
|
|
# Calling a lambda on the fly
|
|
|
|
|
var node = LambdaExpr(node.callee)
|
|
|
|
|
self.error("not implemented")
|
|
|
|
|
# TODO
|
|
|
|
|
of NodeKind.genericExpr:
|
|
|
|
|
# Calling a generic expression
|
|
|
|
|
self.error("not implemented")
|
|
|
|
|
var node = GenericExpr(node.callee)
|
|
|
|
|
# TODO
|
|
|
|
|
else:
|
|
|
|
|
let typ = self.infer(node.callee)
|
|
|
|
|
if typ.isNil():
|
|
|
|
|
@@ -1510,22 +1373,18 @@ proc blockStmt(self: TypeChecker, node: BlockStmt): TypedBlockStmt =
|
|
|
|
|
self.beginScope()
|
|
|
|
|
var body: seq[TypedNode] = @[]
|
|
|
|
|
for decl in node.body:
|
|
|
|
|
body.add(self.check(decl))
|
|
|
|
|
body.add(self.typecheck(decl))
|
|
|
|
|
self.endScope()
|
|
|
|
|
result = newTypedBlockStmt(node, body)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc ifStmt(self: TypeChecker, node: IfStmt): TypedIfStmt =
|
|
|
|
|
## Typechecks if/else statements
|
|
|
|
|
|
|
|
|
|
# Check the condition
|
|
|
|
|
let condition = self.ensureBoolean(node.condition)
|
|
|
|
|
# Check the body
|
|
|
|
|
let then = TypedBlockStmt(self.check(node.thenBranch))
|
|
|
|
|
let then = TypedBlockStmt(self.typecheck(node.thenBranch))
|
|
|
|
|
var otherwise: TypedBlockStmt = nil
|
|
|
|
|
if not node.elseBranch.isNil():
|
|
|
|
|
# Check the else clause, if it exists
|
|
|
|
|
otherwise = TypedBlockStmt(self.check(node.elseBranch))
|
|
|
|
|
otherwise = TypedBlockStmt(self.typecheck(node.elseBranch))
|
|
|
|
|
# Note: Peon enforces the body of loops and conditionals to
|
|
|
|
|
# always be a block statement (for a variety of very good reasons,
|
|
|
|
|
# as it avoids mistakes like the infamous GOTO fail), so the
|
|
|
|
|
@@ -1538,15 +1397,16 @@ proc returnStmt(self: TypeChecker, node: ReturnStmt): TypedReturnStmt =
|
|
|
|
|
|
|
|
|
|
if self.currentFunction.isNil():
|
|
|
|
|
self.error("return outside a function is not allowed", node)
|
|
|
|
|
return newTypedReturnStmt(node, self.infer(node.value))
|
|
|
|
|
if self.currentFunction.valueType.returnType.isNil():
|
|
|
|
|
self.error("cannot return a value from a void function", node.value)
|
|
|
|
|
var returnType = self.check(node.value, self.currentFunction.valueType.returnType, node.value)
|
|
|
|
|
return newTypedReturnStmt(node, returnType)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc whileStmt(self: TypeChecker, node: WhileStmt): TypedNode =
|
|
|
|
|
## Typechecks C-style while loops
|
|
|
|
|
let
|
|
|
|
|
condition = self.ensureBoolean(node.condition) # Check the condition
|
|
|
|
|
body = TypedBlockStmt(self.check(node.body)) # Check the body
|
|
|
|
|
return newTypedWhileStmt(node, body, condition)
|
|
|
|
|
return newTypedWhileStmt(node, TypedBlockStmt(self.typecheck(node.body)),
|
|
|
|
|
self.ensureBoolean(node.condition))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc varDecl(self: TypeChecker, node: VarDecl): TypedVarDecl =
|
|
|
|
|
@@ -1563,7 +1423,7 @@ proc varDecl(self: TypeChecker, node: VarDecl): TypedVarDecl =
|
|
|
|
|
else:
|
|
|
|
|
if node.constant and not node.value.isConst():
|
|
|
|
|
self.error("const initializer must be a value of constant type", node.value)
|
|
|
|
|
init = TypedExpr(self.check(node.value))
|
|
|
|
|
init = TypedExpr(self.inferOrError(node.value))
|
|
|
|
|
typ = init.kind
|
|
|
|
|
if not node.valueType.isNil():
|
|
|
|
|
# Explicit type declaration always takes over
|
|
|
|
|
@@ -1606,12 +1466,12 @@ proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedFunDecl =
|
|
|
|
|
var paramName = Name(kind: Default, ident: parameter.ident, module: self.currentModule, file: self.file, depth: self.scopeDepth,
|
|
|
|
|
isPrivate: true, valueType: nil, owner: self.currentFunction, line: parameter.ident.token.line,
|
|
|
|
|
node: node)
|
|
|
|
|
var default = if not parameter.default.isNil(): self.inferOrError(parameter.default) else: nil
|
|
|
|
|
var default = if not parameter.default.isNil(): self.expression(parameter.default) else: nil
|
|
|
|
|
case parameter.valueType.kind:
|
|
|
|
|
of NodeKind.binaryExpr, NodeKind.unaryExpr:
|
|
|
|
|
# Untagged type union
|
|
|
|
|
paramName.valueType = Type(kind: Union, types: @[])
|
|
|
|
|
self.expandTypeConstraints(parameter.valueType, paramName.valueType.types)
|
|
|
|
|
paramName.valueType = Type(kind: Union, constraints: @[])
|
|
|
|
|
self.expandTypeConstraints(parameter.valueType, paramName.valueType.constraints)
|
|
|
|
|
else:
|
|
|
|
|
paramName.valueType = self.ensureType(parameter.valueType).kind.unwrapType()
|
|
|
|
|
# Ensure the expression represents a type and not a value
|
|
|
|
|
@@ -1621,8 +1481,6 @@ proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedFunDecl =
|
|
|
|
|
self.addName(paramName)
|
|
|
|
|
if not node.returnType.isNil():
|
|
|
|
|
name.valueType.returnType = self.ensureType(node.returnType).kind.unwrapType()
|
|
|
|
|
if not name.valueType.isAuto and self.compare(name.valueType.returnType, "auto".toIntrinsic()):
|
|
|
|
|
name.valueType.isAuto = true
|
|
|
|
|
self.recordCapabilities(name)
|
|
|
|
|
if name.valueType.isBuiltin:
|
|
|
|
|
self.endScope()
|
|
|
|
|
@@ -1639,7 +1497,7 @@ proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedFunDecl =
|
|
|
|
|
let function = self.currentFunction
|
|
|
|
|
self.currentFunction = name
|
|
|
|
|
for decl in BlockStmt(node.body).body:
|
|
|
|
|
result.body.body.add(self.check(decl))
|
|
|
|
|
result.body.body.add(self.typecheck(decl))
|
|
|
|
|
self.endScope()
|
|
|
|
|
# Restores the enclosing function (if any).
|
|
|
|
|
# Makes nested calls work (including recursion)
|
|
|
|
|
@@ -1649,14 +1507,12 @@ proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedFunDecl =
|
|
|
|
|
proc declareGenerics(self: TypeChecker, name: Name) =
|
|
|
|
|
## Helper to declare the generic arguments of the
|
|
|
|
|
## given name, if it has any
|
|
|
|
|
if name.valueType.kind notin [TypeKind.Structure, TypeKind.Function] and not name.valueType.intrinsic:
|
|
|
|
|
self.error("not implemented")
|
|
|
|
|
var
|
|
|
|
|
constraints: seq[tuple[match: bool, kind: Type, value: TypedExpr]] = @[]
|
|
|
|
|
value: Expression
|
|
|
|
|
for gen in name.node.generics.values():
|
|
|
|
|
if gen.constr.isNil():
|
|
|
|
|
constraints = @[(match: true, kind: "any".toIntrinsic(), value: self.infer(value))]
|
|
|
|
|
constraints = @[(match: true, kind: "any".toIntrinsic(), value: self.expression(value))]
|
|
|
|
|
else:
|
|
|
|
|
self.expandTypeConstraints(gen.constr, constraints)
|
|
|
|
|
let generic = Name(kind: Default,
|
|
|
|
|
@@ -1666,11 +1522,11 @@ proc declareGenerics(self: TypeChecker, name: Name) =
|
|
|
|
|
file: self.currentModule.file,
|
|
|
|
|
depth: self.scopeDepth,
|
|
|
|
|
isPrivate: true,
|
|
|
|
|
valueType: Type(kind: Generic, types: constraints, displayName: gen.ident.token.lexeme),
|
|
|
|
|
valueType: Type(kind: Generic, constraints: constraints, displayName: gen.ident.token.lexeme),
|
|
|
|
|
line: gen.ident.token.line,
|
|
|
|
|
)
|
|
|
|
|
self.addName(generic)
|
|
|
|
|
name.valueType.generics[gen.ident.token.lexeme] = generic.valueType.unwrapType()
|
|
|
|
|
name.valueType.generics[gen.ident.token.lexeme] = generic.valueType
|
|
|
|
|
constraints.setLen(0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1679,7 +1535,7 @@ proc typeDecl(self: TypeChecker, node: TypeDecl, name: Name = nil): TypedTypeDec
|
|
|
|
|
var name = name
|
|
|
|
|
if name.isNil():
|
|
|
|
|
name = self.declare(node)
|
|
|
|
|
result = newTypedTypeDecl(node, name, newTable[string, TypedExpr](), nil)
|
|
|
|
|
result = newTypedTypeDecl(node, name, newTable[string, TypedExpr]())
|
|
|
|
|
self.beginScope()
|
|
|
|
|
# Declare the type's generics
|
|
|
|
|
self.declareGenerics(name)
|
|
|
|
|
@@ -1690,7 +1546,7 @@ proc typeDecl(self: TypeChecker, node: TypeDecl, name: Name = nil): TypedTypeDec
|
|
|
|
|
var fieldType: TypedExpr
|
|
|
|
|
var n: Name
|
|
|
|
|
for field in node.fields.values():
|
|
|
|
|
fieldType = self.infer(field.valueType)
|
|
|
|
|
fieldType = self.expression(field.valueType)
|
|
|
|
|
if fieldType.kind.kind != Reference:
|
|
|
|
|
# Check for self-recursion of non-ref types (which would require
|
|
|
|
|
# infinite memory)
|
|
|
|
|
@@ -1716,30 +1572,14 @@ proc typeDecl(self: TypeChecker, node: TypeDecl, name: Name = nil): TypedTypeDec
|
|
|
|
|
case node.value.kind:
|
|
|
|
|
of NodeKind.identExpr:
|
|
|
|
|
# Type alias
|
|
|
|
|
name.valueType = self.inferOrError(node.value).kind
|
|
|
|
|
name.valueType = self.expression(node.value).kind
|
|
|
|
|
of NodeKind.binaryExpr, NodeKind.unaryExpr:
|
|
|
|
|
# Untagged type union
|
|
|
|
|
name.valueType = Type(kind: Union, types: @[], displayName: name.ident.token.lexeme)
|
|
|
|
|
self.expandTypeConstraints(node.value, name.valueType.types)
|
|
|
|
|
name.valueType = Type(kind: Union, constraints: @[], displayName: name.ident.token.lexeme)
|
|
|
|
|
self.expandTypeConstraints(node.value, name.valueType.constraints)
|
|
|
|
|
else:
|
|
|
|
|
# Unreachable due to how we parse things. Still, better be safe
|
|
|
|
|
self.error(&"got node of unexpected type {node.value.kind} at typeDecl()")
|
|
|
|
|
if not node.parent.isNil():
|
|
|
|
|
# Ensure parent is actually a type
|
|
|
|
|
let subtype = self.ensureType(node.parent)
|
|
|
|
|
# Grab its name object
|
|
|
|
|
let parentName = subtype.getName()
|
|
|
|
|
# This should *never* be nil
|
|
|
|
|
if parentName.isNil():
|
|
|
|
|
self.error(&"could not obtain name information for the given object: is it a type?", node.parent)
|
|
|
|
|
result.parent = parentName
|
|
|
|
|
for field in TypeDecl(result.parent.node).fields.values():
|
|
|
|
|
if result.fields.hasKey(field.ident.token.lexeme):
|
|
|
|
|
for f in TypeDecl(result.node).fields.values():
|
|
|
|
|
if f.ident.token.lexeme == field.ident.token.lexeme:
|
|
|
|
|
# This always eventually runs
|
|
|
|
|
self.error(&"cannot to re-declare type member '{field}'", f.ident)
|
|
|
|
|
result.fields[field.ident.token.lexeme] = newTypedExpr(field.ident, result.parent.valueType.fields[field.ident.token.lexeme])
|
|
|
|
|
# Turn the declared type into a typevar so that future references
|
|
|
|
|
# to it will be distinct from its instances
|
|
|
|
|
name.valueType = name.valueType.wrapType()
|
|
|
|
|
@@ -1752,9 +1592,9 @@ proc pragmaExpr(self: TypeChecker, pragma: Pragma) =
|
|
|
|
|
if pragma.name.token.lexeme notin self.pragmas:
|
|
|
|
|
self.error(&"unknown pragma '{pragma.name.token.lexeme}'")
|
|
|
|
|
self.pragmas[pragma.name.token.lexeme].handler(self, pragma, nil)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc check(self: TypeChecker, node: ASTNode): TypedNode =
|
|
|
|
|
|
|
|
|
|
proc typecheck(self: TypeChecker, node: ASTNode): TypedNode =
|
|
|
|
|
## Dispatches typeless AST nodes to typecheck them and turn
|
|
|
|
|
## them into typed ones
|
|
|
|
|
case node.kind:
|
|
|
|
|
@@ -1766,7 +1606,7 @@ proc check(self: TypeChecker, node: ASTNode): TypedNode =
|
|
|
|
|
result = self.expression(Expression(node))
|
|
|
|
|
of NodeKind.exprStmt:
|
|
|
|
|
let statement = ExprStmt(node)
|
|
|
|
|
result = TypedExprStmt(node: statement, expression: TypedExpr(self.check(statement.expression)))
|
|
|
|
|
result = TypedExprStmt(node: statement, expression: TypedExpr(self.typecheck(statement.expression)))
|
|
|
|
|
of NodeKind.whileStmt:
|
|
|
|
|
result = self.whileStmt(WhileStmt(node))
|
|
|
|
|
of NodeKind.blockStmt:
|
|
|
|
|
@@ -1789,7 +1629,7 @@ proc check(self: TypeChecker, node: ASTNode): TypedNode =
|
|
|
|
|
self.error(&"failed to dispatch node of type {node.kind}", node)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
proc check*(self: TypeChecker, tree: ParseTree, file, source: string, showMismatches: bool = false,
|
|
|
|
|
proc typecheck*(self: TypeChecker, tree: ParseTree, file, source: string, showMismatches: bool = false,
|
|
|
|
|
disabledWarnings: seq[WarningKind] = @[]): seq[TypedNode] =
|
|
|
|
|
## Transforms a sequence of typeless AST nodes
|
|
|
|
|
## into a sequence of typed AST nodes
|
|
|
|
|
@@ -1831,7 +1671,7 @@ proc check*(self: TypeChecker, tree: ParseTree, file, source: string, showMismat
|
|
|
|
|
self.addName(main)
|
|
|
|
|
var node: TypedNode
|
|
|
|
|
while not self.done():
|
|
|
|
|
node = self.check(self.step())
|
|
|
|
|
node = self.typecheck(self.step())
|
|
|
|
|
if node.isNil():
|
|
|
|
|
continue
|
|
|
|
|
result.add(node)
|
|
|
|
|
|