Completely rework generics

This commit is contained in:
Mattia Giambirtone 2023-12-05 12:48:41 +01:00
parent 838fc3d5a1
commit a6a944a4fa
Signed by: nocturn9x
GPG Key ID: 8270F9F467971E59
6 changed files with 206 additions and 87 deletions

View File

@ -1,2 +1,2 @@
--hints:off --deepCopy:on --experimental:strictFuncs --hints:off --deepCopy:on --experimental:strictFuncs --exceptions:setjmp
path="src" path="src"

View File

@ -445,7 +445,7 @@ proc generate*(self: BytecodeGenerator, compiled: seq[TypedNode], typeChecker: T
self.currentNode = typedNode self.currentNode = typedNode
let currentFile = self.currentFile let currentFile = self.currentFile
if self.currentNode.node.isDecl(): if self.currentNode.node.isDecl():
self.currentFile = TypedDecl(typedNode).name.owner.ident.token.lexeme self.currentFile = TypedDecl(typedNode).name.module.ident.token.lexeme
case typedNode.node.kind: case typedNode.node.kind:
of exprStmt: of exprStmt:
self.generateExpression(TypedExprStmt(typedNode).expression) self.generateExpression(TypedExprStmt(typedNode).expression)

View File

@ -63,12 +63,12 @@ type
proc validate(self: TypeChecker, node: ASTNode): TypedNode proc validate(self: TypeChecker, node: ASTNode): TypedNode
proc toIntrinsic(name: string): Type proc toIntrinsic(name: string): Type
proc done(self: TypeChecker): bool {.inline.} proc done(self: TypeChecker): bool {.inline.}
proc wrapType(self: Type): Type {.inline.}
proc unwrapType(self: Type): Type {.inline.}
proc toRef(self: Type): Type {.inline.} proc toRef(self: Type): Type {.inline.}
proc toConst(self: Type): Type {.inline.} proc toConst(self: Type): Type {.inline.}
proc toPtr(self: Type): Type {.inline.} proc toPtr(self: Type): Type {.inline.}
proc toLent(self: Type): Type {.inline.} proc toLent(self: Type): Type {.inline.}
proc wrapType(self: Type): Type {.inline.}
proc unwrapType(self: Type): Type {.inline.}
proc handleErrorPragma(self: TypeChecker, pragma: Pragma, name: Name) proc handleErrorPragma(self: TypeChecker, pragma: Pragma, name: Name)
proc handlePurePragma(self: TypeChecker, pragma: Pragma, name: Name) proc handlePurePragma(self: TypeChecker, pragma: Pragma, name: Name)
proc handleMagicPragma(self: TypeChecker, pragma: Pragma, name: Name) proc handleMagicPragma(self: TypeChecker, pragma: Pragma, name: Name)
@ -80,7 +80,7 @@ proc stringify*(self: TypeChecker, typ: TypedNode): string
proc inferOrError*(self: TypeChecker, node: Expression): TypedExpr proc inferOrError*(self: TypeChecker, node: Expression): TypedExpr
proc compare(self: TypeChecker, a, b: Type): bool proc compare(self: TypeChecker, a, b: Type): bool
proc expression(self: TypeChecker, node: Expression): TypedExpr proc expression(self: TypeChecker, node: Expression): TypedExpr
proc specialize(self: TypeChecker, name: Name, args: seq[TypedExpr]): Type proc specialize(self: TypeChecker, name: Name, args: seq[TypedExpr], node: ASTNode = nil): Type
proc declareGenerics(self: TypeChecker, name: Name) proc declareGenerics(self: TypeChecker, name: Name)
proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedFunDecl proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedFunDecl
proc getTypeDistance(self: TypeChecker, a, b: Type): int proc getTypeDistance(self: TypeChecker, a, b: Type): int
@ -151,6 +151,7 @@ proc handleMagicPragma(self: TypeChecker, pragma: Pragma, name: Name) =
let typ = pragma.args[0].token.lexeme[1..^2].toIntrinsic() let typ = pragma.args[0].token.lexeme[1..^2].toIntrinsic()
if typ.isNil(): if typ.isNil():
self.error("'magic' pragma: wrong argument value", pragma.args[0]) self.error("'magic' pragma: wrong argument value", pragma.args[0])
name.valueType = typ.wrapType()
name.valueType.intrinsic = true name.valueType.intrinsic = true
else: else:
self.error("'magic' pragma is not valid in this context") self.error("'magic' pragma is not valid in this context")
@ -190,7 +191,7 @@ proc handleUnsafePragma(self: TypeChecker, pragma: Pragma, name: Name) =
## Handles the "unsafe" pragma ## Handles the "unsafe" pragma
if name.node.kind notin [NodeKind.funDecl, NodeKind.lambdaExpr]: if name.node.kind notin [NodeKind.funDecl, NodeKind.lambdaExpr]:
self.error("'unsafe' pragma is not valid in this context") self.error("'unsafe' pragma is not valid in this context")
name.valueType.safe = false name.valueType.unsafe = true
proc warning(self: TypeChecker, kind: WarningKind, message: string, name: Name = nil, node: ASTNode = nil) = proc warning(self: TypeChecker, kind: WarningKind, message: string, name: Name = nil, node: ASTNode = nil) =
@ -207,8 +208,8 @@ proc warning(self: TypeChecker, kind: WarningKind, message: string, name: Name =
node = name.node node = name.node
if node.isNil(): if node.isNil():
node = self.getCurrentNode() node = self.getCurrentNode()
if not name.belongsTo.isNil(): if not name.owner.isNil():
fn = name.belongsTo.node fn = name.owner.node
else: else:
fn = self.getCurrentFunction() fn = self.getCurrentFunction()
var file = self.file var file = self.file
@ -237,7 +238,13 @@ proc warning(self: TypeChecker, kind: WarningKind, message: string, name: Name =
proc wrapType(self: Type): Type {.inline.} = proc wrapType(self: Type): Type {.inline.} =
## Wraps a type in a typevar ## Wraps a type in a typevar
result = Type(kind: Typevar, wrapped: self) case self.kind:
of Union:
result = Type(kind: Union, types: @[])
for typ in self.types:
result.types.add((match: typ.match, kind: typ.kind.wrapType(), value: typ.value))
else:
result = Type(kind: Typevar, wrapped: self)
proc unwrapType(self: Type): Type {.inline.} = proc unwrapType(self: Type): Type {.inline.} =
@ -246,6 +253,10 @@ proc unwrapType(self: Type): Type {.inline.} =
case self.kind: case self.kind:
of Typevar: of Typevar:
result = self.wrapped result = self.wrapped
of Union:
result = Type(kind: Union, types: @[])
for typ in self.types:
result.types.add((match: typ.match, kind: typ.kind.unwrapType(), value: typ.value))
else: else:
result = self result = self
@ -313,8 +324,6 @@ proc toIntrinsic(name: string): Type =
return Type(kind: Infinity) return Type(kind: Infinity)
of "bool": of "bool":
return Type(kind: Boolean) return Type(kind: Boolean)
of "typevar":
return Type(kind: Typevar, wrapped: "any".toIntrinsic())
of "string": of "string":
return Type(kind: String) return Type(kind: String)
of "pointer": of "pointer":
@ -392,17 +401,12 @@ proc matchUnion(self: TypeChecker, a: Type, b: seq[tuple[match: bool, kind: Type
proc matchGeneric(self: TypeChecker, a: Type, b: seq[tuple[match: bool, kind: Type, value: Expression]]): bool = proc matchGeneric(self: TypeChecker, a: Type, b: seq[tuple[match: bool, kind: Type, value: Expression]]): bool =
## Returns whether a concrete type matches the ## Returns whether a concrete type matches the
## given generic type b ## given generic type b
assert a.kind != Generic
for constraint in b: for constraint in b:
if not self.compare(constraint.kind, a) or not constraint.match: if not self.compare(constraint.kind, a) or not constraint.match:
return false return false
return true return true
proc isType(typ: Type): bool =
return typ.kind in [Structure, Typevar]
proc isAny(typ: Type): bool = proc isAny(typ: Type): bool =
## Returns true if the given type is ## Returns true if the given type is
## of (or contains) the any type. Not ## of (or contains) the any type. Not
@ -419,16 +423,77 @@ proc isAny(typ: Type): bool =
proc compare(self: TypeChecker, a, b: Type): bool = proc compare(self: TypeChecker, a, b: Type): bool =
if a.isNil() or b.isNil():
raise newException(NilAccessDefect, "what the fuck are you doing")
if a.isAny() or b.isAny(): if a.isAny() or b.isAny():
return true return true
if a.kind == b.kind: if a.kind == b.kind:
case a.kind: case a.kind:
of Typevar:
return self.compare(a.wrapped, b.wrapped)
# TODO: Take interfaces into account # TODO: Take interfaces into account
of Structure: of Structure:
# TODO # Compare type names, if they both have it
return false # (some internally generated types may not
# have names)
if a.name.len() > 0 and b.name.len() > 0:
if a.name != b.name:
return false
# Compare fields
var hashSet = initHashSet[string]()
for field in a.fields.keys():
hashSet.incl(field)
for field in b.fields.keys():
hashSet.incl(field)
# Ensure both types have the same field
# names
for field in hashSet:
if field notin a.fields:
return false
if field notin b.fields:
return false
# Ensure fields have matching types
for field in hashSet:
if not self.compare(a.fields[field], b.fields[field]):
return false
hashSet.clear()
# Compare generic arguments
# Check generic types
for generic in a.genericTypes.keys():
hashSet.incl(generic)
for generic in b.genericTypes.keys():
hashSet.incl(generic)
# Ensure both types have the same generic
# argument names
for generic in hashSet:
if generic notin a.genericTypes:
return false
if generic notin b.genericTypes:
return false
for generic in hashSet:
if not self.compare(a.genericTypes[generic], b.genericTypes[generic]):
return false
hashSet.clear()
# Check generic values
for generic in a.genericValues.keys():
hashSet.incl(generic)
for generic in b.genericValues.keys():
hashSet.incl(generic)
# Ensure both types have the same generic
# argument names
for generic in hashSet:
if generic notin a.genericTypes:
return false
if generic notin b.genericTypes:
return false
for generic in hashSet:
if not self.compare(a.genericValues[generic], b.genericValues[generic]):
return false
return true
of Boolean, Infinity, Any, of Boolean, Infinity, Any,
Auto, Char, Byte, String: Auto, Char, Byte, String:
return true return true
@ -440,8 +505,6 @@ proc compare(self: TypeChecker, a, b: Type): bool =
return self.compare(a.value, b.value) return self.compare(a.value, b.value)
of Union: of Union:
return self.compareUnions(a.types, b.types) return self.compareUnions(a.types, b.types)
of Typevar:
return self.compare(a.wrapped, b.wrapped)
of Function: of Function:
# TODO # TODO
return false return false
@ -455,7 +518,7 @@ proc compare(self: TypeChecker, a, b: Type): bool =
return false return false
proc compare*(self: TypeChecker, a, b: Name): bool = proc compare(self: TypeChecker, a, b: Name): bool =
## Compares two names. In addition to checking ## Compares two names. In addition to checking
## that their types match, this function makes ## that their types match, this function makes
## sure the two given objects actually come from ## sure the two given objects actually come from
@ -551,7 +614,7 @@ proc find(self: TypeChecker, name: string, kind: Type = "any".toIntrinsic()): Na
while depth >= 0: while depth >= 0:
if self.names[depth].hasKey(name): if self.names[depth].hasKey(name):
for obj in reversed(self.names[depth][name]): for obj in reversed(self.names[depth][name]):
if obj.owner.absPath != self.currentModule.absPath: if obj.module.absPath != self.currentModule.absPath:
# We don't own this name, but we # We don't own this name, but we
# may still have access to it # may still have access to it
if obj.isPrivate or self.currentModule notin obj.exportedTo: if obj.isPrivate or self.currentModule notin obj.exportedTo:
@ -570,7 +633,6 @@ proc find(self: TypeChecker, name: string, kind: Type = "any".toIntrinsic()): Na
if self.compare(obj.valueType, kind): if self.compare(obj.valueType, kind):
result = obj result = obj
break break
echo "DIOSTRONZO"
if not result.isNil(): if not result.isNil():
break break
@ -599,7 +661,7 @@ proc findAll(self: TypeChecker, name: string, kind: Type = "any".toIntrinsic()):
# module, so we definitely can't use it, or # module, so we definitely can't use it, or
# said module has not explicitly exported it # said module has not explicitly exported it
# to us. If the name is public but not exported # to us. If the name is public but not exported
# in its owner module, then we act as if it's # from its owner module, then we act as if it's
# private. This is to avoid namespace pollution # private. This is to avoid namespace pollution
# from imports (i.e. if module A imports modules # from imports (i.e. if module A imports modules
# C and D and module B imports module A, then B # C and D and module B imports module A, then B
@ -665,8 +727,6 @@ proc stringify*(self: TypeChecker, typ: Type): string =
result &= "32" result &= "32"
of Full: of Full:
result &= "64" result &= "64"
of Typevar:
result = &"typevar[{self.stringify(typ.wrapped)}]"
of Pointer: of Pointer:
result &= &"ptr {self.stringify(typ.value)}" result &= &"ptr {self.stringify(typ.value)}"
of Reference: of Reference:
@ -686,11 +746,7 @@ proc stringify*(self: TypeChecker, typ: Type): string =
result &= "]" result &= "]"
result &= "(" result &= "("
for i, (argName, argType, argDefault) in typ.parameters: for i, (argName, argType, argDefault) in typ.parameters:
result &= &"{argName}: " result &= &"{argName}: {self.stringify(argType)}"
if argType.kind == Generic:
result &= argType.name
else:
result &= self.stringify(argType)
if not argDefault.isNil(): if not argDefault.isNil():
result &= &" = {argDefault.kind}" result &= &" = {argDefault.kind}"
if i < typ.parameters.len() - 1: if i < typ.parameters.len() - 1:
@ -723,6 +779,8 @@ proc stringify*(self: TypeChecker, typ: Type): string =
if not condition.match: if not condition.match:
result &= "~" result &= "~"
result &= self.stringify(condition.kind) result &= self.stringify(condition.kind)
of Typevar:
result &= &"typevar[{self.stringify(typ.wrapped)}]"
else: else:
discard # TODO(?) discard # TODO(?)
@ -755,12 +813,17 @@ proc endScope(self: TypeChecker) =
assert self.scopeDepth == self.names.high() assert self.scopeDepth == self.names.high()
func isType(self: Type): bool = self.kind in [Structure, ]
proc isTypevar(self: Type): bool =
return self.unwrapType() != self
proc check(self: TypeChecker, term, expected: Type, node: ASTNode = nil): Type {.inline, discardable.} = proc check(self: TypeChecker, term, expected: Type, node: ASTNode = nil): Type {.inline, discardable.} =
## Like the other check(), but works with two type objects. ## Like the other check(), but works with two type objects.
## The node is passed in to error() in case of a failure ## The node is passed in to error() in case of a failure
result = term result = term
if not self.compare(result, expected): if not self.compare(result, expected):
self.error(&"expecting expression of type {self.stringify(expected)}, got {self.stringify(result)} instead", node=node) self.error(&"expecting an expression of type {self.stringify(expected)}, got {self.stringify(result)} instead", node=node)
if result.isAny() and not expected.isAny(): if result.isAny() and not expected.isAny():
self.error("any is not a valid type in this context", node) self.error("any is not a valid type in this context", node)
@ -770,10 +833,7 @@ proc check(self: TypeChecker, term: Expression, expected: Type): TypedExpr {.inl
## Raises an error if appropriate and returns the typed expression ## Raises an error if appropriate and returns the typed expression
## otherwise ## otherwise
result = self.inferOrError(term) result = self.inferOrError(term)
if not self.compare(result.kind, expected): self.check(result.kind, expected)
self.error(&"expecting expression of type {self.stringify(expected)}, got {self.stringify(result)} instead", term)
if result.kind.isAny() and not expected.isAny():
self.error("any is not a valid type in this context", term)
proc getTypeDistance(self: TypeChecker, a, b: Type): int = proc getTypeDistance(self: TypeChecker, a, b: Type): int =
@ -874,8 +934,9 @@ proc match(self: TypeChecker, name: string, sig: TypeSignature, node: ASTNode =
## Tries to find a matching type for a given (typeName, typeSignature) pair ## Tries to find a matching type for a given (typeName, typeSignature) pair
## and returns it. In this context, "type signature" means an ordered list of ## and returns it. In this context, "type signature" means an ordered list of
## tuples (paramName, paramType, paramDefault) that represents the arguments we ## tuples (paramName, paramType, paramDefault) that represents the arguments we
## want to instantiate a given (named) object with, be it a function or a type. ## want to instantiate a given named object with, be it a function, a type or an
## The optional node parameter is passed to error() for reporting purposes ## enumeration. The optional node parameter is passed to error() for reporting purposes
## in case of failure
var var
impl: seq[Name] = @[] impl: seq[Name] = @[]
matches: seq[Name] = @[] matches: seq[Name] = @[]
@ -931,14 +992,15 @@ proc match(self: TypeChecker, name: string, sig: TypeSignature, node: ASTNode =
# should we encounter more than one match with the same type distance from our signature that would # 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 # 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 # 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) and # inheritance tree, hence a value of 0 indicates a perfect match (i.e. they're the EXACT same type, structurally
# larger values indicate "less precise" ones # speaking at least), while larger values indicate "less precise" (or "further") ones
for i, n in impl: for i, n in impl:
# Grab all the matches with the smallest type distance
if distances[i] == minDst: if distances[i] == minDst:
matches.add(n) matches.add(n)
case matches.len(): case matches.len():
of 1: of 1:
# Match found, all good! # There's just one match. We're done
result = impl[0] result = impl[0]
if result.kind == NameKind.Var: if result.kind == NameKind.Var:
# Variables bound to other names must always # Variables bound to other names must always
@ -959,7 +1021,7 @@ proc match(self: TypeChecker, name: string, sig: TypeSignature, node: ASTNode =
if not a.isAny() and b.kind.isAny(): if not a.isAny() and b.kind.isAny():
self.error("any is not a valid type in this context", node) self.error("any is not a valid type in this context", node)
else: else:
# TODO # TODO: Enums
discard discard
else: else:
# We either found no matches or too many, woopsie daisy! That's definitely an error # We either found no matches or too many, woopsie daisy! That's definitely an error
@ -1004,25 +1066,52 @@ proc match(self: TypeChecker, name: string, sig: TypeSignature, node: ASTNode =
self.error(msg, node) self.error(msg, node)
proc specialize(self: TypeChecker, name: Name, args: seq[TypedExpr]): Type = proc specialize(self: TypeChecker, name: Name, args: seq[TypedExpr], node: ASTNode = nil): Type =
## Instantiates a generic type ## Instantiates a generic type
let typ = name.valueType let
typ = name.valueType.unwrapType()
expectedCount = typ.genericTypes.len() + typ.genericValues.len()
if expectedCount == 0:
self.error(&"cannot create concrete instance of objects of type {self.stringify(typ)} (type is not a generic)")
if len(args) < expectedCount:
self.error(&"partial generic instantiation is not supported (expecting exactly {expectedCount} arguments, got {len(args)} instead)", node=node)
elif len(args) != expectedCount:
self.error(&"invalid number of arguments supplied for generic instantiation (expecting exactly {expectedCount}, got {len(args)} instead)", node=node)
case typ.kind: case typ.kind:
of TypeKind.Structure: of TypeKind.Structure:
discard result = typ.deepCopy()
result.genericTypes.clear()
result.genericValues.clear()
var replaced = newTable[string, Type]()
var i = 0
for key in typ.genericTypes.keys():
var term = args[i].kind
# Type may not be wrapped yet
if args[i].kind.isType():
term = term.wrapType()
result.genericTypes[key] = self.check(term, typ.genericTypes[key], args[i].node)
replaced[key] = result.genericTypes[key]
inc(i)
# Note how we do not reset i!
for key in typ.genericValues.keys():
var term = args[i].kind
result.genericValues[key] = self.check(term, typ.genericValues[key], args[i].node)
replaced[key] = result.genericValues[key]
inc(i)
for field in TypeDecl(name.node).fields:
if field.valueType.kind == identExpr:
let name = field.valueType.token.lexeme
if name in replaced:
result.fields[name] = replaced[name]
else: else:
self.error(&"cannot instantiate concrete type for object of type {self.stringify(name.valueType)}") self.error(&"cannot create concrete instance of objects of type {self.stringify(typ)}")
proc unpackTypes(self: TypeChecker, condition: Expression, list: var seq[tuple[match: bool, kind: Type, value: Expression]], accept: bool = true, expectTypes: bool = false) = proc unpackTypes(self: TypeChecker, condition: Expression, list: var seq[tuple[match: bool, kind: Type, value: Expression]], accept: bool = true) =
## Recursively unpacks a type constraint ## Recursively unpacks a type constraint
case condition.kind: case condition.kind:
of identExpr, genericExpr: of identExpr, genericExpr:
var typ = self.inferOrError(condition).kind var typ = self.inferOrError(condition).kind
if expectTypes:
if not typ.isType():
self.error(&"expecting a type, got a value of type {self.stringify(typ)} instead", condition)
typ = typ.unwrapType()
if self.compare(typ, "auto".toIntrinsic()): if self.compare(typ, "auto".toIntrinsic()):
self.error("automatic types cannot be used within type constraints", condition) self.error("automatic types cannot be used within type constraints", condition)
@ -1113,11 +1202,11 @@ proc declare(self: TypeChecker, node: ASTNode): Name {.discardable.} =
name = Name(depth: self.scopeDepth, name = Name(depth: self.scopeDepth,
ident: node.name, ident: node.name,
isPrivate: node.isPrivate, isPrivate: node.isPrivate,
owner: self.currentModule, module: self.currentModule,
file: self.file, file: self.file,
valueType: nil, # Done later in varDecl (for better semantics) valueType: nil, # Done later in varDecl (for better semantics)
line: node.token.line, line: node.token.line,
belongsTo: self.currentFunction, owner: self.currentFunction,
kind: NameKind.Var, kind: NameKind.Var,
node: node, node: node,
) )
@ -1138,16 +1227,15 @@ proc declare(self: TypeChecker, node: ASTNode): Name {.discardable.} =
isEnum: node.isEnum) isEnum: node.isEnum)
if node.isRef: if node.isRef:
kind = kind.toRef() kind = kind.toRef()
self.addName(Name(depth: self.scopeDepth, name = Name(depth: self.scopeDepth,
owner: self.currentModule, module: self.currentModule,
node: node, node: node,
ident: node.name, ident: node.name,
line: node.name.token.line, line: node.name.token.line,
isPrivate: node.isPrivate, isPrivate: node.isPrivate,
belongsTo: self.currentFunction, owner: self.currentFunction,
valueType: kind, valueType: kind)
)) self.addName(name)
name = scope[declaredName][^1]
else: else:
discard # TODO: enums discard # TODO: enums
if not name.isNil(): if not name.isNil():
@ -1190,7 +1278,7 @@ proc genericExpr(self: TypeChecker, node: GenericExpr): TypedExpr =
var args: seq[TypedExpr] = @[] var args: seq[TypedExpr] = @[]
for arg in node.args: for arg in node.args:
args.add(self.expression(arg)) args.add(self.expression(arg))
result = newTypedExpr(node, self.specialize(self.findOrError(node.ident.token.lexeme), args)) result = newTypedExpr(node, self.specialize(self.findOrError(node.ident.token.lexeme), args, node))
proc call(self: TypeChecker, node: CallExpr): TypedExpr = proc call(self: TypeChecker, node: CallExpr): TypedExpr =
@ -1272,27 +1360,23 @@ proc refExpr(self: TypeChecker, node: Ref): TypedExpr =
## Typechecks ref expressions ## Typechecks ref expressions
result = self.check(node.value, "typevar".toIntrinsic()) result = self.check(node.value, "typevar".toIntrinsic())
result.kind = result.kind.toRef() result.kind = result.kind.toRef()
result.kind = result.kind.wrapType()
proc constExpr(self: TypeChecker, node: ast.Const): TypedExpr = proc constExpr(self: TypeChecker, node: ast.Const): TypedExpr =
## Typechecks const expressions ## Typechecks const expressions
var kind = "typevar".toIntrinsic() var kind = "any".toIntrinsic().toConst()
kind.wrapped = kind.wrapped.toConst()
result = self.check(node.value, kind) result = self.check(node.value, kind)
result.kind = result.kind.toConst() result.kind = result.kind.toConst()
result.kind = result.kind.wrapType()
proc lentExpr(self: TypeChecker, node: ast.Lent): TypedExpr = proc lentExpr(self: TypeChecker, node: ast.Lent): TypedExpr =
## Typechecks lent expressions ## Typechecks lent expressions
var kind = "typevar".toIntrinsic()
# Only match references # Only match references
kind.wrapped = kind.wrapped.toRef() var kind = "any".toIntrinsic().toRef()
result = self.check(node.value, kind) result = self.check(node.value, kind)
# Wrap the result back # Wrap the result back
result.kind = result.kind.toLent() result.kind = result.kind.toLent()
result.kind = result.kind.wrapType()
#[ #[
method assignment(self: BytecodeCompiler, node: ASTNode, compile: bool = true): Type {.discardable.} = method assignment(self: BytecodeCompiler, node: ASTNode, compile: bool = true): Type {.discardable.} =
@ -1353,7 +1437,6 @@ proc expression(self: TypeChecker, node: Expression): TypedExpr =
of ptrExpr: of ptrExpr:
result = self.check(Ref(node).value, "typevar".toIntrinsic()) result = self.check(Ref(node).value, "typevar".toIntrinsic())
result.kind = result.kind.toPtr() result.kind = result.kind.toPtr()
result.kind = result.kind.wrapType()
of NodeKind.lentExpr: of NodeKind.lentExpr:
result = self.lentExpr(ast.Lent(node)) result = self.lentExpr(ast.Lent(node))
of NodeKind.constExpr: of NodeKind.constExpr:
@ -1453,8 +1536,8 @@ proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedFunDecl =
if not node.returnType.isNil(): if not node.returnType.isNil():
# The function needs a return type too! # The function needs a return type too!
name.valueType.returnType = self.inferOrError(node.returnType).kind name.valueType.returnType = self.inferOrError(node.returnType).kind
if name.valueType.returnType.kind != Generic: # TODO
name.valueType.returnType = self.check(node.returnType, "typevar".toIntrinsic()).kind.unwrapType() # name.valueType.returnType = self.check(node.returnType, "typevar".toIntrinsic()).kind
if not name.valueType.isAuto and self.compare(name.valueType.returnType, "auto".toIntrinsic()): if not name.valueType.isAuto and self.compare(name.valueType.returnType, "auto".toIntrinsic()):
name.valueType.isAuto = true name.valueType.isAuto = true
if node.body.isNil(): if node.body.isNil():
@ -1479,7 +1562,7 @@ proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedFunDecl =
proc declareGenerics(self: TypeChecker, name: Name) = proc declareGenerics(self: TypeChecker, name: Name) =
## Helper to declare the generic arguments of the ## Helper to declare the generic arguments of the
## given name, if it has any ## given name, if it has any
if name.valueType.kind != TypeKind.Structure: if name.valueType.kind notin [TypeKind.Structure, TypeKind.Function]:
return return
var var
constraints: seq[tuple[match: bool, kind: Type, value: Expression]] = @[] constraints: seq[tuple[match: bool, kind: Type, value: Expression]] = @[]
@ -1488,8 +1571,40 @@ proc declareGenerics(self: TypeChecker, name: Name) =
if gen.cond.isNil(): if gen.cond.isNil():
constraints = @[(match: true, kind: "any".toIntrinsic(), value: value)] constraints = @[(match: true, kind: "any".toIntrinsic(), value: value)]
else: else:
self.unpackTypes(gen.cond, constraints, expectTypes=true) self.unpackTypes(gen.cond, constraints)
# TODO let generic = Name(kind: Default,
ident: gen.name,
module: self.currentModule,
owner: self.currentFunction,
file: self.currentModule.file,
depth: self.scopeDepth,
isPrivate: true,
valueType: Type(kind: Union, types: constraints),
line: gen.name.token.line,
)
self.addName(generic)
name.valueType.genericTypes[gen.name.token.lexeme] = generic.valueType
constraints.setLen(0)
for gen in name.node.genericValues:
if gen.cond.isNil():
constraints = @[(match: true, kind: "any".toIntrinsic(), value: value)]
else:
self.unpackTypes(gen.cond, constraints)
let generic = Name(kind: Default,
ident: gen.name,
module: self.currentModule,
owner: self.currentFunction,
file: self.currentModule.file,
depth: self.scopeDepth,
isPrivate: true,
valueType: Type(kind: Union, types: constraints).unwrapType(),
line: gen.name.token.line,
)
self.addName(generic)
name.valueType.genericValues[gen.name.token.lexeme] = generic.valueType
constraints.setLen(0)
proc typeDecl(self: TypeChecker, node: TypeDecl, name: Name = nil): TypedTypeDecl = proc typeDecl(self: TypeChecker, node: TypeDecl, name: Name = nil): TypedTypeDecl =
@ -1561,7 +1676,10 @@ proc typeDecl(self: TypeChecker, node: TypeDecl, name: Name = nil): TypedTypeDec
# This always eventually runs # This always eventually runs
self.error(&"cannot to re-declare type member '{field}'", f.name) self.error(&"cannot to re-declare type member '{field}'", f.name)
result.fields[field.name.token.lexeme] = newTypedExpr(field.name, result.parent.valueType.fields[field.name.token.lexeme]) result.fields[field.name.token.lexeme] = newTypedExpr(field.name, result.parent.valueType.fields[field.name.token.lexeme])
# Turn the declared type into a typevar so that future references
# to it will be distinct from its instances
if not name.valueType.intrinsic:
name.valueType = name.valueType.wrapType()
# TODO: Check interfaces # TODO: Check interfaces
self.endScope() self.endScope()

View File

@ -39,7 +39,6 @@ type
EnumEntry, EnumEntry,
Reference, Reference,
Pointer, Pointer,
Generic,
Union, Union,
Function, Function,
Lent, Lent,
@ -74,7 +73,7 @@ type
isAuto*: bool isAuto*: bool
parameters*: TypeSignature parameters*: TypeSignature
returnType*: Type returnType*: Type
safe*: bool unsafe*: bool
of Typevar: of Typevar:
wrapped*: Type wrapped*: Type
of Structure: of Structure:
@ -124,7 +123,7 @@ type
# The name's identifier # The name's identifier
ident*: IdentExpr ident*: IdentExpr
# Owner of the identifier (module) # Owner of the identifier (module)
owner*: Name module*: Name
# File where the name is declared # File where the name is declared
file*: string file*: string
# Scope depth # Scope depth
@ -134,8 +133,8 @@ type
# The type of the name's associated # The type of the name's associated
# value # value
valueType*: Type valueType*: Type
# The function that owns this name (may be nil!) # The function that "owns" this name (may be nil!)
belongsTo*: Name owner*: Name
# Where is this node declared in its file? # Where is this node declared in its file?
line*: int line*: int
# The AST node associated with this node. This # The AST node associated with this node. This

View File

@ -855,9 +855,11 @@ proc getRelativeBoundaries*(self: ASTNode): tuple[start, stop: int] =
of genericExpr: of genericExpr:
var self = GenericExpr(self) var self = GenericExpr(self)
let ident = getRelativeBoundaries(self.ident) let ident = getRelativeBoundaries(self.ident)
var stop: int = ident.stop + 2 var stop: int = ident.stop
if self.args.len() > 0: if self.args.len() > 0:
stop = getRelativeBoundaries(self.args[^1]).stop stop = getRelativeBoundaries(self.args[^1]).stop
# Take the "]" into account
inc(stop)
result = (ident.start, stop) result = (ident.start, stop)
of refExpr: of refExpr:
var self = Ref(self) var self = Ref(self)

View File

@ -118,7 +118,7 @@ proc runFile(filename: string, fromString: bool = false, dump: bool = true, brea
# *Technically* an expression statement has no type, but that isn't really useful for debug # *Technically* an expression statement has no type, but that isn't really useful for debug
# purposes, so we print the type of the expression within it instead # purposes, so we print the type of the expression within it instead
let exprNode = TypedExprStmt(typedNode).expression let exprNode = TypedExprStmt(typedNode).expression
styledEcho fgGreen, &"\t{typedNode.node} -> {exprNode.node} -> {typeChecker.stringify(TypedExprStmt(typedNode).expression)}\n" styledEcho fgGreen, &"\t{typedNode.node} (inner) -> {typeChecker.stringify(exprNode.kind)}\n"
else: else:
styledEcho fgGreen, &"\t{typedNode.node} -> {typeChecker.stringify(typedNode)}\n" styledEcho fgGreen, &"\t{typedNode.node} -> {typeChecker.stringify(typedNode)}\n"
case backend: case backend: