Completely rework generics
This commit is contained in:
parent
838fc3d5a1
commit
a6a944a4fa
2
nim.cfg
2
nim.cfg
|
@ -1,2 +1,2 @@
|
|||
--hints:off --deepCopy:on --experimental:strictFuncs
|
||||
--hints:off --deepCopy:on --experimental:strictFuncs --exceptions:setjmp
|
||||
path="src"
|
|
@ -445,7 +445,7 @@ proc generate*(self: BytecodeGenerator, compiled: seq[TypedNode], typeChecker: T
|
|||
self.currentNode = typedNode
|
||||
let currentFile = self.currentFile
|
||||
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:
|
||||
of exprStmt:
|
||||
self.generateExpression(TypedExprStmt(typedNode).expression)
|
||||
|
|
|
@ -63,12 +63,12 @@ type
|
|||
proc validate(self: TypeChecker, node: ASTNode): TypedNode
|
||||
proc toIntrinsic(name: string): Type
|
||||
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 toConst(self: Type): Type {.inline.}
|
||||
proc toPtr(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 handlePurePragma(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 compare(self: TypeChecker, a, b: Type): bool
|
||||
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 funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedFunDecl
|
||||
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()
|
||||
if typ.isNil():
|
||||
self.error("'magic' pragma: wrong argument value", pragma.args[0])
|
||||
name.valueType = typ.wrapType()
|
||||
name.valueType.intrinsic = true
|
||||
else:
|
||||
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
|
||||
if name.node.kind notin [NodeKind.funDecl, NodeKind.lambdaExpr]:
|
||||
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) =
|
||||
|
@ -207,8 +208,8 @@ proc warning(self: TypeChecker, kind: WarningKind, message: string, name: Name =
|
|||
node = name.node
|
||||
if node.isNil():
|
||||
node = self.getCurrentNode()
|
||||
if not name.belongsTo.isNil():
|
||||
fn = name.belongsTo.node
|
||||
if not name.owner.isNil():
|
||||
fn = name.owner.node
|
||||
else:
|
||||
fn = self.getCurrentFunction()
|
||||
var file = self.file
|
||||
|
@ -237,7 +238,13 @@ proc warning(self: TypeChecker, kind: WarningKind, message: string, name: Name =
|
|||
|
||||
proc wrapType(self: Type): Type {.inline.} =
|
||||
## 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.} =
|
||||
|
@ -246,6 +253,10 @@ proc unwrapType(self: Type): Type {.inline.} =
|
|||
case self.kind:
|
||||
of Typevar:
|
||||
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:
|
||||
result = self
|
||||
|
||||
|
@ -313,8 +324,6 @@ proc toIntrinsic(name: string): Type =
|
|||
return Type(kind: Infinity)
|
||||
of "bool":
|
||||
return Type(kind: Boolean)
|
||||
of "typevar":
|
||||
return Type(kind: Typevar, wrapped: "any".toIntrinsic())
|
||||
of "string":
|
||||
return Type(kind: String)
|
||||
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 =
|
||||
## Returns whether a concrete type matches the
|
||||
## given generic type b
|
||||
assert a.kind != Generic
|
||||
for constraint in b:
|
||||
if not self.compare(constraint.kind, a) or not constraint.match:
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
proc isType(typ: Type): bool =
|
||||
return typ.kind in [Structure, Typevar]
|
||||
|
||||
|
||||
proc isAny(typ: Type): bool =
|
||||
## Returns true if the given type is
|
||||
## of (or contains) the any type. Not
|
||||
|
@ -419,16 +423,77 @@ proc isAny(typ: 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():
|
||||
return true
|
||||
if a.kind == b.kind:
|
||||
case a.kind:
|
||||
of Typevar:
|
||||
return self.compare(a.wrapped, b.wrapped)
|
||||
# TODO: Take interfaces into account
|
||||
of Structure:
|
||||
# TODO
|
||||
return false
|
||||
# Compare type names, if they both have it
|
||||
# (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,
|
||||
Auto, Char, Byte, String:
|
||||
return true
|
||||
|
@ -440,8 +505,6 @@ proc compare(self: TypeChecker, a, b: Type): bool =
|
|||
return self.compare(a.value, b.value)
|
||||
of Union:
|
||||
return self.compareUnions(a.types, b.types)
|
||||
of Typevar:
|
||||
return self.compare(a.wrapped, b.wrapped)
|
||||
of Function:
|
||||
# TODO
|
||||
return false
|
||||
|
@ -455,7 +518,7 @@ proc compare(self: TypeChecker, a, b: Type): bool =
|
|||
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
|
||||
## that their types match, this function makes
|
||||
## 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:
|
||||
if self.names[depth].hasKey(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
|
||||
# may still have access to it
|
||||
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):
|
||||
result = obj
|
||||
break
|
||||
echo "DIOSTRONZO"
|
||||
|
||||
if not result.isNil():
|
||||
break
|
||||
|
@ -599,7 +661,7 @@ proc findAll(self: TypeChecker, name: string, kind: Type = "any".toIntrinsic()):
|
|||
# module, so we definitely can't use it, or
|
||||
# said module has not explicitly exported it
|
||||
# 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
|
||||
# from imports (i.e. if module A imports modules
|
||||
# C and D and module B imports module A, then B
|
||||
|
@ -665,8 +727,6 @@ proc stringify*(self: TypeChecker, typ: Type): string =
|
|||
result &= "32"
|
||||
of Full:
|
||||
result &= "64"
|
||||
of Typevar:
|
||||
result = &"typevar[{self.stringify(typ.wrapped)}]"
|
||||
of Pointer:
|
||||
result &= &"ptr {self.stringify(typ.value)}"
|
||||
of Reference:
|
||||
|
@ -686,11 +746,7 @@ proc stringify*(self: TypeChecker, typ: Type): string =
|
|||
result &= "]"
|
||||
result &= "("
|
||||
for i, (argName, argType, argDefault) in typ.parameters:
|
||||
result &= &"{argName}: "
|
||||
if argType.kind == Generic:
|
||||
result &= argType.name
|
||||
else:
|
||||
result &= self.stringify(argType)
|
||||
result &= &"{argName}: {self.stringify(argType)}"
|
||||
if not argDefault.isNil():
|
||||
result &= &" = {argDefault.kind}"
|
||||
if i < typ.parameters.len() - 1:
|
||||
|
@ -723,6 +779,8 @@ proc stringify*(self: TypeChecker, typ: Type): string =
|
|||
if not condition.match:
|
||||
result &= "~"
|
||||
result &= self.stringify(condition.kind)
|
||||
of Typevar:
|
||||
result &= &"typevar[{self.stringify(typ.wrapped)}]"
|
||||
else:
|
||||
discard # TODO(?)
|
||||
|
||||
|
@ -755,12 +813,17 @@ proc endScope(self: TypeChecker) =
|
|||
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.} =
|
||||
## Like the other check(), but works with two type objects.
|
||||
## The node is passed in to error() in case of a failure
|
||||
result = term
|
||||
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():
|
||||
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
|
||||
## otherwise
|
||||
result = self.inferOrError(term)
|
||||
if not self.compare(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)
|
||||
self.check(result.kind, expected)
|
||||
|
||||
|
||||
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
|
||||
## and returns it. In this context, "type signature" means an ordered list of
|
||||
## tuples (paramName, paramType, paramDefault) that represents the arguments we
|
||||
## want to instantiate a given (named) object with, be it a function or a type.
|
||||
## The optional node parameter is passed to error() for reporting purposes
|
||||
## want to instantiate a given named object with, be it a function, a type or an
|
||||
## enumeration. The optional node parameter is passed to error() for reporting purposes
|
||||
## in case of failure
|
||||
var
|
||||
impl: 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
|
||||
# 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) and
|
||||
# larger values indicate "less precise" ones
|
||||
# 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" (or "further") ones
|
||||
for i, n in impl:
|
||||
# Grab all the matches with the smallest type distance
|
||||
if distances[i] == minDst:
|
||||
matches.add(n)
|
||||
case matches.len():
|
||||
of 1:
|
||||
# Match found, all good!
|
||||
# There's just one match. We're done
|
||||
result = impl[0]
|
||||
if result.kind == NameKind.Var:
|
||||
# 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():
|
||||
self.error("any is not a valid type in this context", node)
|
||||
else:
|
||||
# TODO
|
||||
# TODO: Enums
|
||||
discard
|
||||
else:
|
||||
# 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)
|
||||
|
||||
|
||||
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
|
||||
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:
|
||||
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:
|
||||
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
|
||||
case condition.kind:
|
||||
of identExpr, genericExpr:
|
||||
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()):
|
||||
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,
|
||||
ident: node.name,
|
||||
isPrivate: node.isPrivate,
|
||||
owner: self.currentModule,
|
||||
module: self.currentModule,
|
||||
file: self.file,
|
||||
valueType: nil, # Done later in varDecl (for better semantics)
|
||||
line: node.token.line,
|
||||
belongsTo: self.currentFunction,
|
||||
owner: self.currentFunction,
|
||||
kind: NameKind.Var,
|
||||
node: node,
|
||||
)
|
||||
|
@ -1138,16 +1227,15 @@ proc declare(self: TypeChecker, node: ASTNode): Name {.discardable.} =
|
|||
isEnum: node.isEnum)
|
||||
if node.isRef:
|
||||
kind = kind.toRef()
|
||||
self.addName(Name(depth: self.scopeDepth,
|
||||
owner: self.currentModule,
|
||||
node: node,
|
||||
ident: node.name,
|
||||
line: node.name.token.line,
|
||||
isPrivate: node.isPrivate,
|
||||
belongsTo: self.currentFunction,
|
||||
valueType: kind,
|
||||
))
|
||||
name = scope[declaredName][^1]
|
||||
name = Name(depth: self.scopeDepth,
|
||||
module: self.currentModule,
|
||||
node: node,
|
||||
ident: node.name,
|
||||
line: node.name.token.line,
|
||||
isPrivate: node.isPrivate,
|
||||
owner: self.currentFunction,
|
||||
valueType: kind)
|
||||
self.addName(name)
|
||||
else:
|
||||
discard # TODO: enums
|
||||
if not name.isNil():
|
||||
|
@ -1190,7 +1278,7 @@ proc genericExpr(self: TypeChecker, node: GenericExpr): TypedExpr =
|
|||
var args: seq[TypedExpr] = @[]
|
||||
for arg in node.args:
|
||||
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 =
|
||||
|
@ -1272,27 +1360,23 @@ proc refExpr(self: TypeChecker, node: Ref): TypedExpr =
|
|||
## Typechecks ref expressions
|
||||
result = self.check(node.value, "typevar".toIntrinsic())
|
||||
result.kind = result.kind.toRef()
|
||||
result.kind = result.kind.wrapType()
|
||||
|
||||
|
||||
proc constExpr(self: TypeChecker, node: ast.Const): TypedExpr =
|
||||
## Typechecks const expressions
|
||||
var kind = "typevar".toIntrinsic()
|
||||
kind.wrapped = kind.wrapped.toConst()
|
||||
var kind = "any".toIntrinsic().toConst()
|
||||
result = self.check(node.value, kind)
|
||||
result.kind = result.kind.toConst()
|
||||
result.kind = result.kind.wrapType()
|
||||
|
||||
|
||||
proc lentExpr(self: TypeChecker, node: ast.Lent): TypedExpr =
|
||||
## Typechecks lent expressions
|
||||
var kind = "typevar".toIntrinsic()
|
||||
|
||||
# Only match references
|
||||
kind.wrapped = kind.wrapped.toRef()
|
||||
var kind = "any".toIntrinsic().toRef()
|
||||
result = self.check(node.value, kind)
|
||||
# Wrap the result back
|
||||
result.kind = result.kind.toLent()
|
||||
result.kind = result.kind.wrapType()
|
||||
|
||||
#[
|
||||
method assignment(self: BytecodeCompiler, node: ASTNode, compile: bool = true): Type {.discardable.} =
|
||||
|
@ -1353,7 +1437,6 @@ proc expression(self: TypeChecker, node: Expression): TypedExpr =
|
|||
of ptrExpr:
|
||||
result = self.check(Ref(node).value, "typevar".toIntrinsic())
|
||||
result.kind = result.kind.toPtr()
|
||||
result.kind = result.kind.wrapType()
|
||||
of NodeKind.lentExpr:
|
||||
result = self.lentExpr(ast.Lent(node))
|
||||
of NodeKind.constExpr:
|
||||
|
@ -1453,8 +1536,8 @@ proc funDecl(self: TypeChecker, node: FunDecl, name: Name = nil): TypedFunDecl =
|
|||
if not node.returnType.isNil():
|
||||
# The function needs a return type too!
|
||||
name.valueType.returnType = self.inferOrError(node.returnType).kind
|
||||
if name.valueType.returnType.kind != Generic:
|
||||
name.valueType.returnType = self.check(node.returnType, "typevar".toIntrinsic()).kind.unwrapType()
|
||||
# TODO
|
||||
# name.valueType.returnType = self.check(node.returnType, "typevar".toIntrinsic()).kind
|
||||
if not name.valueType.isAuto and self.compare(name.valueType.returnType, "auto".toIntrinsic()):
|
||||
name.valueType.isAuto = true
|
||||
if node.body.isNil():
|
||||
|
@ -1479,7 +1562,7 @@ 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 != TypeKind.Structure:
|
||||
if name.valueType.kind notin [TypeKind.Structure, TypeKind.Function]:
|
||||
return
|
||||
var
|
||||
constraints: seq[tuple[match: bool, kind: Type, value: Expression]] = @[]
|
||||
|
@ -1488,8 +1571,40 @@ proc declareGenerics(self: TypeChecker, name: Name) =
|
|||
if gen.cond.isNil():
|
||||
constraints = @[(match: true, kind: "any".toIntrinsic(), value: value)]
|
||||
else:
|
||||
self.unpackTypes(gen.cond, constraints, expectTypes=true)
|
||||
# TODO
|
||||
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),
|
||||
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 =
|
||||
|
@ -1561,7 +1676,10 @@ proc typeDecl(self: TypeChecker, node: TypeDecl, name: Name = nil): TypedTypeDec
|
|||
# This always eventually runs
|
||||
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])
|
||||
|
||||
# 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
|
||||
self.endScope()
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ type
|
|||
EnumEntry,
|
||||
Reference,
|
||||
Pointer,
|
||||
Generic,
|
||||
Union,
|
||||
Function,
|
||||
Lent,
|
||||
|
@ -74,7 +73,7 @@ type
|
|||
isAuto*: bool
|
||||
parameters*: TypeSignature
|
||||
returnType*: Type
|
||||
safe*: bool
|
||||
unsafe*: bool
|
||||
of Typevar:
|
||||
wrapped*: Type
|
||||
of Structure:
|
||||
|
@ -124,7 +123,7 @@ type
|
|||
# The name's identifier
|
||||
ident*: IdentExpr
|
||||
# Owner of the identifier (module)
|
||||
owner*: Name
|
||||
module*: Name
|
||||
# File where the name is declared
|
||||
file*: string
|
||||
# Scope depth
|
||||
|
@ -134,8 +133,8 @@ type
|
|||
# The type of the name's associated
|
||||
# value
|
||||
valueType*: Type
|
||||
# The function that owns this name (may be nil!)
|
||||
belongsTo*: Name
|
||||
# The function that "owns" this name (may be nil!)
|
||||
owner*: Name
|
||||
# Where is this node declared in its file?
|
||||
line*: int
|
||||
# The AST node associated with this node. This
|
||||
|
|
|
@ -855,9 +855,11 @@ proc getRelativeBoundaries*(self: ASTNode): tuple[start, stop: int] =
|
|||
of genericExpr:
|
||||
var self = GenericExpr(self)
|
||||
let ident = getRelativeBoundaries(self.ident)
|
||||
var stop: int = ident.stop + 2
|
||||
var stop: int = ident.stop
|
||||
if self.args.len() > 0:
|
||||
stop = getRelativeBoundaries(self.args[^1]).stop
|
||||
# Take the "]" into account
|
||||
inc(stop)
|
||||
result = (ident.start, stop)
|
||||
of refExpr:
|
||||
var self = Ref(self)
|
||||
|
|
|
@ -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
|
||||
# purposes, so we print the type of the expression within it instead
|
||||
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:
|
||||
styledEcho fgGreen, &"\t{typedNode.node} -> {typeChecker.stringify(typedNode)}\n"
|
||||
case backend:
|
||||
|
|
Loading…
Reference in New Issue