More fixes to generics
This commit is contained in:
parent
34d5f77f65
commit
60d9b3c37e
|
@ -151,8 +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
|
||||
name.valueType = typ
|
||||
else:
|
||||
self.error("'magic' pragma is not valid in this context")
|
||||
|
||||
|
@ -176,7 +175,6 @@ proc handleWarnPragma(self: TypeChecker, pragma: Pragma, name: Name) =
|
|||
self.error("'warn' pragma: wrong type of argument (constant string expected)")
|
||||
self.warning(UserWarning, pragma.args[0].token.lexeme[1..^2])
|
||||
|
||||
|
||||
|
||||
proc handlePurePragma(self: TypeChecker, pragma: Pragma, name: Name) =
|
||||
## Handles the "pure" pragma
|
||||
|
@ -289,51 +287,51 @@ proc toIntrinsic(name: string): Type =
|
|||
## otherwise
|
||||
case name:
|
||||
of "any":
|
||||
return Type(kind: Any)
|
||||
return Type(kind: Any, intrinsic: true)
|
||||
of "auto":
|
||||
return Type(kind: Auto)
|
||||
return Type(kind: Auto, intrinsic: true)
|
||||
of "int64":
|
||||
return Type(kind: Integer, size: LongLong, signed: true)
|
||||
return Type(kind: Integer, size: LongLong, signed: true, intrinsic: true)
|
||||
of "uint64":
|
||||
return Type(kind: Integer, size: LongLong, signed: false)
|
||||
return Type(kind: Integer, size: LongLong, signed: false, intrinsic: true)
|
||||
of "int32":
|
||||
return Type(kind: Integer, size: Long, signed: true)
|
||||
return Type(kind: Integer, size: Long, signed: true, intrinsic: true)
|
||||
of "uint32":
|
||||
return Type(kind: Integer, size: Long, signed: false)
|
||||
return Type(kind: Integer, size: Long, signed: false, intrinsic: true)
|
||||
of "int16":
|
||||
return Type(kind: Integer, size: Short, signed: true)
|
||||
return Type(kind: Integer, size: Short, signed: true, intrinsic: true)
|
||||
of "uint16":
|
||||
return Type(kind: Integer, size: Short, signed: false)
|
||||
return Type(kind: Integer, size: Short, signed: false, intrinsic: true)
|
||||
of "int8":
|
||||
return Type(kind: Integer, size: Tiny, signed: true)
|
||||
return Type(kind: Integer, size: Tiny, signed: true, intrinsic: true)
|
||||
of "uint8":
|
||||
return Type(kind: Integer, size: Tiny, signed: false)
|
||||
return Type(kind: Integer, size: Tiny, signed: false, intrinsic: true)
|
||||
of "float64":
|
||||
return Type(kind: Float, width: Full)
|
||||
return Type(kind: Float, width: Full, intrinsic: true)
|
||||
of "float32":
|
||||
return Type(kind: Float, width: Half)
|
||||
return Type(kind: Float, width: Half, intrinsic: true)
|
||||
of "byte":
|
||||
return Type(kind: Byte)
|
||||
return Type(kind: Byte, intrinsic: true)
|
||||
of "char":
|
||||
return Type(kind: Char)
|
||||
return Type(kind: Char, intrinsic: true)
|
||||
of "NaN":
|
||||
return Type(kind: TypeKind.Nan)
|
||||
return Type(kind: TypeKind.Nan, intrinsic: true)
|
||||
of "Inf":
|
||||
return Type(kind: Infinity, positive: true)
|
||||
return Type(kind: Infinity, positive: true, intrinsic: true)
|
||||
of "NegInf":
|
||||
return Type(kind: Infinity)
|
||||
return Type(kind: Infinity, intrinsic: true)
|
||||
of "bool":
|
||||
return Type(kind: Boolean)
|
||||
return Type(kind: Boolean, intrinsic: true)
|
||||
of "string":
|
||||
return Type(kind: String)
|
||||
return Type(kind: String, intrinsic: true)
|
||||
of "pointer":
|
||||
return Type(kind: Pointer, value: "any".toIntrinsic())
|
||||
return Type(kind: Pointer, value: "any".toIntrinsic(), intrinsic: true)
|
||||
of "lent":
|
||||
return Type(kind: TypeKind.Lent, value: "any".toIntrinsic())
|
||||
return Type(kind: TypeKind.Lent, value: "any".toIntrinsic(), intrinsic: true)
|
||||
of "const":
|
||||
return Type(kind: TypeKind.Const, value: "any".toIntrinsic())
|
||||
return Type(kind: TypeKind.Const, value: "any".toIntrinsic(), intrinsic: true)
|
||||
of "ref":
|
||||
return Type(kind: Reference, value: "any".toIntrinsic())
|
||||
return Type(kind: Reference, value: "any".toIntrinsic(), intrinsic: true)
|
||||
|
||||
|
||||
|
||||
|
@ -436,6 +434,7 @@ proc compare(self: TypeChecker, a, b: Type): bool =
|
|||
# have names)
|
||||
if a.name.len() > 0 and b.name.len() > 0:
|
||||
if a.name != b.name:
|
||||
echo "fail: name"
|
||||
return false
|
||||
# Compare fields
|
||||
var hashSet = initHashSet[string]()
|
||||
|
@ -447,12 +446,15 @@ proc compare(self: TypeChecker, a, b: Type): bool =
|
|||
# names
|
||||
for field in hashSet:
|
||||
if field notin a.fields:
|
||||
echo &"fail: {field} notin a.fields"
|
||||
return false
|
||||
if field notin b.fields:
|
||||
echo &"fail: {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]):
|
||||
echo &"fail: compare(a.{field}, b.{field})"
|
||||
return false
|
||||
hashSet.clear()
|
||||
# Compare generic arguments
|
||||
|
@ -485,9 +487,9 @@ proc compare(self: TypeChecker, a, b: Type): bool =
|
|||
# Ensure both types have the same generic
|
||||
# argument names
|
||||
for generic in hashSet:
|
||||
if generic notin a.genericTypes:
|
||||
if generic notin a.genericValues:
|
||||
return false
|
||||
if generic notin b.genericTypes:
|
||||
if generic notin b.genericValues:
|
||||
return false
|
||||
|
||||
for generic in hashSet:
|
||||
|
@ -526,7 +528,7 @@ proc compare(self: TypeChecker, a, b: Name): bool =
|
|||
## exact object)
|
||||
if not self.compare(a.valueType, b.valueType):
|
||||
return false
|
||||
return a.owner == b.owner
|
||||
return a.module == b.module
|
||||
|
||||
|
||||
proc literal(self: TypeChecker, node: LiteralExpr): TypedExpr =
|
||||
|
@ -653,7 +655,7 @@ proc findAll(self: TypeChecker, name: string, kind: Type = "any".toIntrinsic()):
|
|||
while depth >= 0:
|
||||
if self.names[depth].hasKey(name):
|
||||
for obj in 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:
|
||||
|
@ -786,6 +788,7 @@ proc stringify*(self: TypeChecker, typ: Type): string =
|
|||
|
||||
|
||||
proc stringify*(self: TypeChecker, typ: TypedNode): string =
|
||||
|
||||
if typ.node.isConst():
|
||||
return self.stringify(TypedExpr(typ).kind)
|
||||
case typ.node.kind:
|
||||
|
@ -796,7 +799,7 @@ proc stringify*(self: TypeChecker, typ: TypedNode): string =
|
|||
result = self.stringify(TypedExpr(typ).kind)
|
||||
else:
|
||||
# TODO
|
||||
return "void"
|
||||
return &"?: {typ[]}"
|
||||
|
||||
|
||||
proc beginScope(self: TypeChecker) =
|
||||
|
@ -1085,24 +1088,23 @@ proc specialize(self: TypeChecker, name: Name, args: seq[TypedExpr], node: ASTNo
|
|||
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)
|
||||
result.genericTypes[key] = self.check(args[i].kind, 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)
|
||||
# echo args[i].kind, "\n"
|
||||
# echo typ.genericValues[key], "\n\n"
|
||||
result.genericValues[key] = self.check(args[i].kind, 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]
|
||||
let
|
||||
fieldName = field.name.token.lexeme
|
||||
fieldType = field.valueType.token.lexeme
|
||||
if fieldType in replaced:
|
||||
result.fields[fieldName] = replaced[fieldType]
|
||||
else:
|
||||
self.error(&"cannot create concrete instance of objects of type {self.stringify(typ)}")
|
||||
|
||||
|
@ -1182,8 +1184,8 @@ proc addName(self: TypeChecker, name: Name) =
|
|||
if name.valueType.kind == TypeKind.Function:
|
||||
# We don't check for name clashes for functions because self.match() does that
|
||||
continue
|
||||
if (obj.kind in [NameKind.Var, NameKind.Module] or obj.valueType.kind in [TypeKind.Structure, TypeKind.EnumEntry]) and name.owner == obj.owner:
|
||||
self.error(&"re-declaration of '{obj.ident.token.lexeme}' is not allowed (previously declared in {obj.owner.ident.token.lexeme}:{obj.ident.token.line}:{obj.ident.token.relPos.start})", name.node)
|
||||
if (obj.kind in [NameKind.Var, NameKind.Module] or obj.valueType.kind in [TypeKind.Structure, TypeKind.EnumEntry]) and name.module == obj.module:
|
||||
self.error(&"re-declaration of '{obj.ident.token.lexeme}' is not allowed (previously declared in {obj.module.ident.token.lexeme}:{obj.ident.token.line}:{obj.ident.token.relPos.start})", name.node)
|
||||
else:
|
||||
scope[name.ident.token.lexeme] = @[]
|
||||
scope[name.ident.token.lexeme].add(name)
|
||||
|
@ -1378,42 +1380,6 @@ proc lentExpr(self: TypeChecker, node: ast.Lent): TypedExpr =
|
|||
# Wrap the result back
|
||||
result.kind = result.kind.toLent()
|
||||
|
||||
#[
|
||||
method assignment(self: BytecodeCompiler, node: ASTNode, compile: bool = true): Type {.discardable.} =
|
||||
## Typechecks assignment expressions
|
||||
case node.kind:
|
||||
of assignExpr:
|
||||
let node = AssignExpr(node)
|
||||
let name = IdentExpr(node.name)
|
||||
var r = self.resolveOrError(name)
|
||||
if r.constant:
|
||||
self.error(&"cannot assign to '{name.token.lexeme}' (value is a constant)", name)
|
||||
elif r.mutable:
|
||||
self.error(&"cannot reassign '{name.token.lexeme}' (value is immutable)", name)
|
||||
self.check(node.value, r.valueType)
|
||||
self.expression(node.value, compile)
|
||||
var position = r.position
|
||||
if r.depth < self.depth and r.belongsTo != self.currentFunction:
|
||||
self.warning(WarningKind.MutateOuterScope, &"mutation of '{r.ident.token.lexeme}' declared in outer scope ({r.owner.file}.pn:{r.ident.token.line}:{r.ident.token.relPos.start})", nil, node)
|
||||
result = r.valueType
|
||||
if not compile:
|
||||
return
|
||||
self.emitByte(StoreVar, node.token.line)
|
||||
self.emitBytes(position.toTriple(), node.token.line)
|
||||
of setItemExpr:
|
||||
let node = SetItemExpr(node)
|
||||
let name = IdentExpr(node.name)
|
||||
var r = self.resolveOrError(name)
|
||||
if r.constant:
|
||||
self.error(&"cannot assign to '{name.token.lexeme}' (value is a constant)", name)
|
||||
elif r.mutable:
|
||||
self.error(&"cannot reassign '{name.token.lexeme}' (value is immutable)", name)
|
||||
if r.valueType.kind != Structure:
|
||||
self.error("only types have fields", node)
|
||||
else:
|
||||
self.error(&"invalid AST node of kind {node.kind} at assignment(): {node} (This is an internal error and most likely a bug)")
|
||||
]#
|
||||
|
||||
|
||||
proc expression(self: TypeChecker, node: Expression): TypedExpr =
|
||||
## Typechecks expressions
|
||||
|
@ -1579,7 +1545,7 @@ proc declareGenerics(self: TypeChecker, name: Name) =
|
|||
file: self.currentModule.file,
|
||||
depth: self.scopeDepth,
|
||||
isPrivate: true,
|
||||
valueType: Type(kind: Union, types: constraints),
|
||||
valueType: if constraints.len() > 1: Type(kind: Union, types: constraints) else: constraints[0].kind,
|
||||
line: gen.name.token.line,
|
||||
)
|
||||
self.addName(generic)
|
||||
|
@ -1598,7 +1564,7 @@ proc declareGenerics(self: TypeChecker, name: Name) =
|
|||
file: self.currentModule.file,
|
||||
depth: self.scopeDepth,
|
||||
isPrivate: true,
|
||||
valueType: Type(kind: Union, types: constraints).unwrapType(),
|
||||
valueType: (if constraints.len() > 1: Type(kind: Union, types: constraints) else: constraints[0].kind).unwrapType(),
|
||||
line: gen.name.token.line,
|
||||
)
|
||||
self.addName(generic)
|
||||
|
@ -1678,8 +1644,7 @@ proc typeDecl(self: TypeChecker, node: TypeDecl, name: Name = nil): TypedTypeDec
|
|||
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()
|
||||
name.valueType = name.valueType.wrapType()
|
||||
# TODO: Check interfaces
|
||||
self.endScope()
|
||||
|
||||
|
|
|
@ -1025,18 +1025,19 @@ proc parseGenericConstraint(self: Parser, endToken: TokenType or string): Expres
|
|||
proc parseGenerics(self: Parser, decl: Declaration) =
|
||||
## Parses generics in declarations
|
||||
var gen: tuple[name: IdentExpr, cond: Expression]
|
||||
while not self.check(">") and not self.done():
|
||||
self.expect(Identifier, "expecting generic type name")
|
||||
gen.name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||
gen.name.file = self.file
|
||||
if self.match(":"):
|
||||
gen.cond = self.parseGenericConstraint(">")
|
||||
else:
|
||||
gen.cond = nil
|
||||
decl.genericTypes.add(gen)
|
||||
if not self.match(Comma):
|
||||
break
|
||||
self.expect(">", "unterminated generic declaration")
|
||||
if self.match("<"):
|
||||
while not self.check(">") and not self.done():
|
||||
self.expect(Identifier, "expecting generic type name")
|
||||
gen.name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||
gen.name.file = self.file
|
||||
if self.match(":"):
|
||||
gen.cond = self.parseGenericConstraint(">")
|
||||
else:
|
||||
gen.cond = nil
|
||||
decl.genericTypes.add(gen)
|
||||
if not self.match(Comma):
|
||||
break
|
||||
self.expect(">", "unterminated generic declaration")
|
||||
if self.match(LeftBracket):
|
||||
while not self.check(RightBracket) and not self.done():
|
||||
self.expect(Identifier, "expecting generic type name")
|
||||
|
@ -1078,7 +1079,7 @@ proc funDecl(self: Parser, isAsync: bool = false, isGenerator: bool = false,
|
|||
if self.match("*"):
|
||||
FunDecl(self.currentFunction).isPrivate = false
|
||||
self.checkDecl(FunDecl(self.currentFunction).isPrivate)
|
||||
if self.match("<"):
|
||||
if self.check(["<", "["]):
|
||||
self.parseGenerics(self.currentFunction)
|
||||
elif not isLambda and (self.check([LeftBrace, LeftParen]) or self.check(":")):
|
||||
# We do a bit of hacking to pretend we never
|
||||
|
@ -1276,7 +1277,7 @@ proc typeDecl(self: Parser): TypeDecl =
|
|||
var name = newIdentExpr(self.peek(-1), self.scopeDepth)
|
||||
result = newTypeDecl(name, @[], @[], true, token, @[], @[], @[], nil, false, false)
|
||||
result.file = self.file
|
||||
if self.match("<"):
|
||||
if self.check(["<", "["]):
|
||||
self.parseGenerics(result)
|
||||
result.isPrivate = not self.match("*")
|
||||
self.checkDecl(result.isPrivate)
|
||||
|
@ -1321,7 +1322,7 @@ proc typeDecl(self: Parser): TypeDecl =
|
|||
self.expect(Identifier, "expecting variant name")
|
||||
variant.name = newIdentExpr(self.peek(-1))
|
||||
variant.token = variant.name.token
|
||||
if self.match(LeftBracket):
|
||||
if self.check(["[", "<"]):
|
||||
self.parseGenerics(variant)
|
||||
if self.match("{"):
|
||||
variant.fields = self.parseTypeFields()
|
||||
|
|
|
@ -184,6 +184,7 @@ proc runFile(filename: string, fromString: bool = false, dump: bool = true, brea
|
|||
print(exc)
|
||||
except TypeCheckError as exc:
|
||||
print(exc)
|
||||
raise
|
||||
except CodeGenError as exc:
|
||||
var file = exc.file
|
||||
if file notin ["<string>", ""]:
|
||||
|
|
|
@ -8,8 +8,13 @@ type Test<T: int64>[V: int64] = object {
|
|||
value: V;
|
||||
}
|
||||
|
||||
type Test2<T: Test>[V: Test] = object {
|
||||
value: V;
|
||||
}
|
||||
|
||||
Test[int64, 1]; # Works
|
||||
# Test[int64, int64]; # Error: expecting an expression of type int64, got typevar[int64] instead
|
||||
# Test[1, int64]; # Error: expecting an expression of type typevar[int64], got int64 instead
|
||||
|
||||
Test[int64, 1]; # Works
|
||||
# Test[int64, int64]; # Error: expecting an expression of type int64, got typevar[int64] instead
|
||||
# Test[1, int64]; # Error: expecting an expression of type typevar[int64], got int64 instead
|
||||
Test2[Test, Test[int64, 1]]; # Also works
|
||||
|
||||
|
|
Loading…
Reference in New Issue