Initial work on fixing the hellish nightmare of bugs of this repo
This commit is contained in:
parent
919cc25579
commit
20a2f07eba
|
@ -25,13 +25,13 @@ when not defined(gcArc) and not defined(gcOrc):
|
|||
|
||||
|
||||
import std/math
|
||||
import std/strformat
|
||||
import std/segfaults
|
||||
import std/strutils
|
||||
import std/sets
|
||||
import std/monotimes
|
||||
|
||||
when debugVM or debugMem or debugGC or debugAlloc:
|
||||
import std/strformat
|
||||
import std/sequtils
|
||||
import std/terminal
|
||||
|
||||
|
@ -1065,8 +1065,12 @@ proc run*(self: var PeonVM, chunk: Chunk, breakpoints: seq[uint64] = @[], repl:
|
|||
self.lastDebugCommand = ""
|
||||
try:
|
||||
self.dispatch()
|
||||
except Defect as e:
|
||||
stderr.writeLine(&"Fatal error at bytecode offset {self.ip - 1}: {e.name} -> {e.msg}")
|
||||
except CatchableError as e:
|
||||
stderr.writeLine(&"Fatal error at bytecode offset {self.ip - 1}: {e.name} -> {e.msg}")
|
||||
except NilAccessDefect:
|
||||
stderr.writeLine("Memory Access Violation: SIGSEGV")
|
||||
stderr.writeLine(&"Memory Access Violation (bytecode offset {self.ip}): SIGSEGV")
|
||||
quit(1)
|
||||
if not repl:
|
||||
# We clean up after ourselves!
|
||||
|
|
|
@ -64,6 +64,7 @@ type
|
|||
isBuiltin*: bool
|
||||
case kind*: TypeKind:
|
||||
of Function:
|
||||
nameObj*: Name
|
||||
isLambda*: bool
|
||||
isGenerator*: bool
|
||||
isCoroutine*: bool
|
||||
|
@ -99,7 +100,7 @@ type
|
|||
## A name enumeration type
|
||||
None, Module, Argument, Var, Function, CustomType, Enum
|
||||
|
||||
Name* = ref object of RootObj
|
||||
Name* = ref object
|
||||
## A generic name object
|
||||
|
||||
# Type of the identifier (NOT of the value!)
|
||||
|
@ -345,6 +346,22 @@ proc step*(self: Compiler): ASTNode {.inline.} =
|
|||
self.current += 1
|
||||
|
||||
|
||||
proc wrap*(self: Type): Type =
|
||||
## Wraps a type in a typevar if it's not already
|
||||
## wrapped
|
||||
if self.kind != Typevar:
|
||||
return Type(kind: Typevar, wrapped: self)
|
||||
return self
|
||||
|
||||
|
||||
proc unwrap*(self: Type): Type =
|
||||
## Unwraps a typevar if it's not already
|
||||
## unwrapped
|
||||
if self.kind == Typevar:
|
||||
return self.wrapped
|
||||
return self
|
||||
|
||||
|
||||
# Peon's type inference and name resolution system is very flexible
|
||||
# and can be reused across multiple compilation backends
|
||||
|
||||
|
@ -420,25 +437,27 @@ proc compareUnions*(self: Compiler, a, b: seq[tuple[match: bool, kind: Type]]):
|
|||
proc compare*(self: Compiler, a, b: Type): bool =
|
||||
## Compares two type objects
|
||||
## for equality
|
||||
result = false
|
||||
|
||||
# Note: 'All' is a type internal to the peon
|
||||
# compiler that cannot be generated from user
|
||||
# code in any way. It's used mostly for matching
|
||||
# function return types (at least until we don't
|
||||
# have return type inference) and it matches any
|
||||
# type, including nil (nim's nil, not our nil)
|
||||
# type, including nil (nim's nil, aka no type at all,
|
||||
# not peon's nil which still has a type)
|
||||
if a.isNil():
|
||||
return b.isNil() or b.kind == All
|
||||
elif b.isNil():
|
||||
if b.isNil():
|
||||
return a.isNil() or a.kind == All
|
||||
elif a.kind == All or b.kind == All:
|
||||
if a.kind == All or b.kind == All:
|
||||
return true
|
||||
if a.kind == b.kind:
|
||||
# Here we compare types with the same kind discriminant
|
||||
case a.kind:
|
||||
of Int8, UInt8, Int16, UInt16, Int32,
|
||||
UInt32, Int64, UInt64, Float32, Float64,
|
||||
Char, Byte, String, Nil, TypeKind.Nan, Bool, TypeKind.Inf, Any:
|
||||
Char, Byte, String, Nil, TypeKind.Nan, Bool,
|
||||
TypeKind.Inf, Any, Auto:
|
||||
return true
|
||||
of Typevar:
|
||||
return self.compare(a.wrapped, b.wrapped)
|
||||
|
@ -481,17 +500,21 @@ proc compare*(self: Compiler, a, b: Type): bool =
|
|||
return true
|
||||
else:
|
||||
discard # TODO: Custom types, enums
|
||||
elif a.kind == Union:
|
||||
if a.kind == Typevar:
|
||||
return self.compare(a.wrapped, b)
|
||||
if b.kind == Typevar:
|
||||
return self.compare(a, b.wrapped)
|
||||
if a.kind == Union:
|
||||
for constraint in a.types:
|
||||
if self.compare(constraint.kind, b) and constraint.match:
|
||||
return true
|
||||
return false
|
||||
elif b.kind == Union:
|
||||
if b.kind == Union:
|
||||
for constraint in b.types:
|
||||
if self.compare(constraint.kind, a) and constraint.match:
|
||||
return true
|
||||
return false
|
||||
elif a.kind == Generic:
|
||||
if a.kind == Generic:
|
||||
if a.asUnion:
|
||||
for constraint in a.cond:
|
||||
if self.compare(constraint.kind, b) and constraint.match:
|
||||
|
@ -502,7 +525,7 @@ proc compare*(self: Compiler, a, b: Type): bool =
|
|||
if not self.compare(constraint.kind, b) or not constraint.match:
|
||||
return false
|
||||
return true
|
||||
elif b.kind == Generic:
|
||||
if b.kind == Generic:
|
||||
if b.asUnion:
|
||||
for constraint in b.cond:
|
||||
if self.compare(constraint.kind, a) and constraint.match:
|
||||
|
@ -513,7 +536,7 @@ proc compare*(self: Compiler, a, b: Type): bool =
|
|||
if not self.compare(constraint.kind, a) or not constraint.match:
|
||||
return false
|
||||
return true
|
||||
elif a.kind == Any or b.kind == Any:
|
||||
if a.kind == Any or b.kind == Any:
|
||||
# Here we already know that neither of
|
||||
# these types are nil, so we can always
|
||||
# just return true
|
||||
|
@ -526,47 +549,47 @@ proc toIntrinsic*(name: string): Type =
|
|||
## type if it is valid and returns nil
|
||||
## otherwise
|
||||
if name == "any":
|
||||
return Type(kind: Any)
|
||||
return Type(kind: Any, isBuiltin: true)
|
||||
elif name == "all":
|
||||
return Type(kind: All)
|
||||
return Type(kind: All, isBuiltin: true)
|
||||
elif name == "auto":
|
||||
return Type(kind: Auto)
|
||||
return Type(kind: Auto, isBuiltin: true)
|
||||
elif name in ["int", "int64", "i64"]:
|
||||
return Type(kind: Int64)
|
||||
return Type(kind: Int64, isBuiltin: true)
|
||||
elif name in ["uint64", "u64", "uint"]:
|
||||
return Type(kind: UInt64)
|
||||
return Type(kind: UInt64, isBuiltin: true)
|
||||
elif name in ["int32", "i32"]:
|
||||
return Type(kind: Int32)
|
||||
return Type(kind: Int32, isBuiltin: true)
|
||||
elif name in ["uint32", "u32"]:
|
||||
return Type(kind: UInt32)
|
||||
return Type(kind: UInt32, isBuiltin: true)
|
||||
elif name in ["int16", "i16", "short"]:
|
||||
return Type(kind: Int16)
|
||||
return Type(kind: Int16, isBuiltin: true)
|
||||
elif name in ["uint16", "u16"]:
|
||||
return Type(kind: UInt16)
|
||||
return Type(kind: UInt16, isBuiltin: true)
|
||||
elif name in ["int8", "i8"]:
|
||||
return Type(kind: Int8)
|
||||
return Type(kind: Int8, isBuiltin: true)
|
||||
elif name in ["uint8", "u8"]:
|
||||
return Type(kind: UInt8)
|
||||
return Type(kind: UInt8, isBuiltin: true)
|
||||
elif name in ["f64", "float", "float64"]:
|
||||
return Type(kind: Float64)
|
||||
return Type(kind: Float64, isBuiltin: true)
|
||||
elif name in ["f32", "float32"]:
|
||||
return Type(kind: Float32)
|
||||
return Type(kind: Float32, isBuiltin: true)
|
||||
elif name in ["byte", "b"]:
|
||||
return Type(kind: Byte)
|
||||
return Type(kind: Byte, isBuiltin: true)
|
||||
elif name in ["char", "c"]:
|
||||
return Type(kind: Char)
|
||||
return Type(kind: Char, isBuiltin: true)
|
||||
elif name == "nan":
|
||||
return Type(kind: TypeKind.Nan)
|
||||
return Type(kind: TypeKind.Nan, isBuiltin: true)
|
||||
elif name == "nil":
|
||||
return Type(kind: Nil)
|
||||
return Type(kind: Nil, isBuiltin: true)
|
||||
elif name == "inf":
|
||||
return Type(kind: TypeKind.Inf)
|
||||
return Type(kind: TypeKind.Inf, isBuiltin: true)
|
||||
elif name == "bool":
|
||||
return Type(kind: Bool)
|
||||
return Type(kind: Bool, isBuiltin: true)
|
||||
elif name == "typevar":
|
||||
return Type(kind: Typevar)
|
||||
return Type(kind: Typevar, isBuiltin: true)
|
||||
elif name == "string":
|
||||
return Type(kind: String)
|
||||
return Type(kind: String, isBuiltin: true)
|
||||
|
||||
|
||||
proc infer*(self: Compiler, node: LiteralExpr): Type =
|
||||
|
@ -577,7 +600,7 @@ proc infer*(self: Compiler, node: LiteralExpr): Type =
|
|||
of intExpr, binExpr, octExpr, hexExpr:
|
||||
let size = node.token.lexeme.split("'")
|
||||
if size.len() == 1:
|
||||
return Type(kind: Int64)
|
||||
return Type(kind: Int64, isBuiltin: true)
|
||||
let typ = size[1].toIntrinsic()
|
||||
if not self.compare(typ, nil):
|
||||
return typ
|
||||
|
@ -586,18 +609,18 @@ proc infer*(self: Compiler, node: LiteralExpr): Type =
|
|||
of floatExpr:
|
||||
let size = node.token.lexeme.split("'")
|
||||
if size.len() == 1:
|
||||
return Type(kind: Float64)
|
||||
return Type(kind: Float64, isBuiltin: true)
|
||||
let typ = size[1].toIntrinsic()
|
||||
if not typ.isNil():
|
||||
return typ
|
||||
else:
|
||||
self.error(&"invalid type specifier '{size[1]}' for float", node)
|
||||
of trueExpr:
|
||||
return Type(kind: Bool)
|
||||
return Type(kind: Bool, isBuiltin: true)
|
||||
of falseExpr:
|
||||
return Type(kind: Bool)
|
||||
return Type(kind: Bool, isBuiltin: true)
|
||||
of strExpr:
|
||||
return Type(kind: String)
|
||||
return Type(kind: String, isBuiltin: true)
|
||||
else:
|
||||
discard # Unreachable
|
||||
|
||||
|
@ -847,6 +870,10 @@ proc match*(self: Compiler, name: string, kind: Type, node: ASTNode = nil, allow
|
|||
self.error(&"expecting an implementation for function '{impl[0].ident.token.lexeme}' declared in module '{impl[0].owner.ident.token.lexeme}' at line {impl[0].ident.token.line} of type '{self.stringify(impl[0].valueType)}'")
|
||||
result = impl[0]
|
||||
result.resolved = true
|
||||
if result.kind == NameKind.Var:
|
||||
# We found a function bound to a variable,
|
||||
# so we return the original function's name object
|
||||
result = result.valueType.nameObj
|
||||
for (a, b) in zip(result.valueType.args, kind.args):
|
||||
if not a.kind.isAny() and b.kind.isAny():
|
||||
self.error("any is not a valid type in this context", node)
|
||||
|
@ -947,6 +974,7 @@ proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
|
|||
if node.generics.len() > 0:
|
||||
fn.isGeneric = true
|
||||
self.names.add(fn)
|
||||
fn.valueType.nameObj = fn
|
||||
self.prepareFunction(fn)
|
||||
n = fn
|
||||
of NodeKind.importStmt:
|
||||
|
|
|
@ -106,7 +106,6 @@ method dispatchDelayedPragmas(self: BytecodeCompiler, name: Name)
|
|||
proc funDecl(self: BytecodeCompiler, node: FunDecl, name: Name)
|
||||
proc compileModule(self: BytecodeCompiler, module: Name)
|
||||
proc generateCall(self: BytecodeCompiler, fn: Name, args: seq[Expression], line: int)
|
||||
method prepareFunction(self: BytecodeCompiler, fn: Name)
|
||||
# End of forward declarations
|
||||
|
||||
|
||||
|
@ -466,7 +465,7 @@ proc handleBuiltinFunction(self: BytecodeCompiler, fn: Type, args: seq[Expressio
|
|||
"Identity": Identity
|
||||
}.to_table()
|
||||
if fn.builtinOp == "print":
|
||||
var typ = self.inferOrError(args[0])
|
||||
var typ = self.inferOrError(args[0]).unwrap()
|
||||
case typ.kind:
|
||||
of Int64:
|
||||
self.emitByte(PrintInt64, line)
|
||||
|
@ -799,9 +798,7 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
|
|||
## "Prepares" a function declaration by declaring
|
||||
## its arguments and typechecking it
|
||||
|
||||
# First we declare the function's generics, if it has any.
|
||||
# This is because the function's return type may in itself
|
||||
# be a generic, so it needs to exist first
|
||||
# First we declare the function's generics, if it has any
|
||||
var constraints: seq[tuple[match: bool, kind: Type]] = @[]
|
||||
for gen in fn.node.generics:
|
||||
self.unpackTypes(gen.cond, constraints)
|
||||
|
@ -829,8 +826,9 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
|
|||
self.error("cannot declare more than 16777215 variables at a time")
|
||||
inc(self.stackIndex)
|
||||
typ = self.inferOrError(argument.valueType)
|
||||
if typ.kind == Typevar:
|
||||
typ = typ.wrapped
|
||||
if self.compare(typ, "auto".toIntrinsic()):
|
||||
fn.valueType.isAuto = true
|
||||
typ = "any".toIntrinsic()
|
||||
self.names.add(Name(depth: fn.depth + 1,
|
||||
isPrivate: true,
|
||||
owner: fn.owner,
|
||||
|
@ -857,8 +855,8 @@ method prepareFunction(self: BytecodeCompiler, fn: Name) =
|
|||
# The function needs a return type too!
|
||||
if not node.returnType.isNil():
|
||||
fn.valueType.returnType = self.inferOrError(node.returnType)
|
||||
if fn.valueType.returnType.kind == Typevar:
|
||||
fn.valueType.returnType = fn.valueType.returnType.wrapped
|
||||
if self.compare(fn.valueType.returnType, "auto".toIntrinsic()):
|
||||
fn.valueType.isAuto = true
|
||||
fn.position = self.stackIndex
|
||||
self.stackIndex = idx
|
||||
if node.isTemplate:
|
||||
|
@ -1045,17 +1043,17 @@ method literal(self: BytecodeCompiler, node: ASTNode, compile: bool = true): Typ
|
|||
## as singletons, strings and numbers
|
||||
case node.kind:
|
||||
of trueExpr:
|
||||
result = Type(kind: Bool)
|
||||
result = "bool".toIntrinsic()
|
||||
if compile:
|
||||
self.emitByte(LoadTrue, node.token.line)
|
||||
of falseExpr:
|
||||
result = Type(kind: Bool)
|
||||
result = "bool".toIntrinsic()
|
||||
if compile:
|
||||
self.emitByte(LoadFalse, node.token.line)
|
||||
of strExpr:
|
||||
result = Type(kind: String)
|
||||
result = "string".toIntrinsic()
|
||||
if compile:
|
||||
self.emitConstant(LiteralExpr(node), Type(kind: String))
|
||||
self.emitConstant(LiteralExpr(node), result)
|
||||
of intExpr:
|
||||
let y = IntExpr(node)
|
||||
let kind = self.infer(y)
|
||||
|
@ -1159,7 +1157,7 @@ method unary(self: BytecodeCompiler, node: UnaryExpr, compile: bool = true): Typ
|
|||
method binary(self: BytecodeCompiler, node: BinaryExpr, compile: bool = true): Type {.discardable.} =
|
||||
## Compiles all binary expressions
|
||||
var default: Expression
|
||||
let fn = Type(kind: Function, returnType: Type(kind: Any), args: @[("", self.inferOrError(node.a), default), ("", self.inferOrError(node.b), default)])
|
||||
let fn = Type(kind: Function, returnType: "any".toIntrinsic(), args: @[("", self.inferOrError(node.a), default), ("", self.inferOrError(node.b), default)])
|
||||
var impl = self.match(node.token.lexeme, fn, node)
|
||||
result = impl.valueType
|
||||
if impl.isGeneric:
|
||||
|
@ -1186,7 +1184,7 @@ method identifier(self: BytecodeCompiler, node: IdentExpr, name: Name = nil, com
|
|||
if s.kind == NameKind.CustomType:
|
||||
# This makes it so that the type of
|
||||
# a type comes out as "typevar"
|
||||
result = Type(kind: Typevar, wrapped: result)
|
||||
result = result.wrap()
|
||||
if not compile:
|
||||
return result
|
||||
var node = s.ident
|
||||
|
@ -1209,29 +1207,16 @@ method identifier(self: BytecodeCompiler, node: IdentExpr, name: Name = nil, com
|
|||
# to have something on the stack to pop off (just to act as
|
||||
# a placeholder)
|
||||
self.emitByte(LoadNil, node.token.line)
|
||||
elif s.valueType.isBuiltin:
|
||||
case s.ident.token.lexeme:
|
||||
of "nil":
|
||||
self.emitByte(LoadNil, node.token.line)
|
||||
of "nan":
|
||||
self.emitByte(LoadNan, node.token.line)
|
||||
of "inf":
|
||||
self.emitByte(LoadInf, node.token.line)
|
||||
else:
|
||||
discard # Unreachable
|
||||
else:
|
||||
if not s.belongsTo.isNil() and s.belongsTo.valueType.fun.kind == funDecl and FunDecl(s.belongsTo.valueType.fun).isTemplate:
|
||||
discard
|
||||
if s.depth > 0:
|
||||
# Loads a regular variable from the current frame
|
||||
self.emitByte(LoadVar, s.ident.token.line)
|
||||
else:
|
||||
if s.depth > 0:
|
||||
# Loads a regular variable from the current frame
|
||||
self.emitByte(LoadVar, s.ident.token.line)
|
||||
else:
|
||||
# Loads a global variable from an absolute stack
|
||||
# position
|
||||
self.emitByte(LoadGlobal, s.ident.token.line)
|
||||
# No need to check for -1 here: we already did a nil check above!
|
||||
self.emitBytes(s.position.toTriple(), s.ident.token.line)
|
||||
# Loads a global variable from an absolute stack
|
||||
# position
|
||||
self.emitByte(LoadGlobal, s.ident.token.line)
|
||||
# No need to check for -1 here: we already did a nil check above!
|
||||
self.emitBytes(s.position.toTriple(), s.ident.token.line)
|
||||
|
||||
|
||||
method assignment(self: BytecodeCompiler, node: ASTNode, compile: bool = true): Type {.discardable.} =
|
||||
|
@ -1332,7 +1317,7 @@ method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type
|
|||
case node.callee.kind:
|
||||
of NodeKind.identExpr:
|
||||
# Calls like hi()
|
||||
var impl = self.match(IdentExpr(node.callee).name.lexeme, Type(kind: Function, returnType: Type(kind: All), args: args), node)
|
||||
var impl = self.match(IdentExpr(node.callee).name.lexeme, Type(kind: Function, returnType: "all".toIntrinsic(), args: args), node)
|
||||
result = impl.valueType
|
||||
if impl.isGeneric:
|
||||
result = self.specialize(impl.valueType, argExpr)
|
||||
|
@ -1421,8 +1406,6 @@ method call(self: BytecodeCompiler, node: CallExpr, compile: bool = true): Type
|
|||
self.error(&"expression has no type", node)
|
||||
else:
|
||||
self.error(&"object of type '{self.stringify(typ)}' is not callable", node)
|
||||
if not result.isNil() and result.kind == Typevar:
|
||||
result = result.wrapped
|
||||
|
||||
|
||||
method getItemExpr(self: BytecodeCompiler, node: GetItemExpr, compile: bool = true, matching: Type = nil): Type {.discardable.} =
|
||||
|
@ -1626,7 +1609,7 @@ method expression(self: BytecodeCompiler, node: Expression, compile: bool = true
|
|||
proc ifStmt(self: BytecodeCompiler, node: IfStmt) =
|
||||
## Compiles if/else statements for conditional
|
||||
## execution of code
|
||||
self.check(node.condition, Type(kind: Bool))
|
||||
self.check(node.condition, "bool".toIntrinsic())
|
||||
self.expression(node.condition)
|
||||
let jump = self.emitJump(JumpIfFalsePop, node.token.line)
|
||||
self.statement(node.thenBranch)
|
||||
|
@ -1640,7 +1623,7 @@ proc ifStmt(self: BytecodeCompiler, node: IfStmt) =
|
|||
proc whileStmt(self: BytecodeCompiler, node: WhileStmt) =
|
||||
## Compiles C-style while loops and
|
||||
## desugared C-style for loops
|
||||
self.check(node.condition, Type(kind: Bool))
|
||||
self.check(node.condition, "bool".toIntrinsic())
|
||||
let start = self.chunk.code.high()
|
||||
self.expression(node.condition)
|
||||
let jump = self.emitJump(JumpIfFalsePop, node.token.line)
|
||||
|
@ -1686,7 +1669,7 @@ proc returnStmt(self: BytecodeCompiler, node: ReturnStmt) =
|
|||
elif not self.currentFunction.valueType.returnType.isNil() and node.value.isNil():
|
||||
self.error("bare return statement is only allowed in void functions", node)
|
||||
if not node.value.isNil():
|
||||
if self.currentFunction.valueType.returnType.kind == Auto:
|
||||
if self.compare(self.currentFunction.valueType.returnType, "auto".toIntrinsic()):
|
||||
self.currentFunction.valueType.returnType = self.inferOrError(node.value)
|
||||
self.check(node.value, self.currentFunction.valueType.returnType)
|
||||
self.expression(node.value)
|
||||
|
@ -1852,7 +1835,7 @@ proc switchStmt(self: BytecodeCompiler, node: SwitchStmt) =
|
|||
self.emitByte(DupTop, branch.body.token.line)
|
||||
self.expression(branch.cond)
|
||||
# We look for a matching equality implementation
|
||||
fn = Type(kind: Function, returnType: Type(kind: Bool), args: @[("", typeOfA, default), ("", self.inferOrError(branch.cond), default)])
|
||||
fn = Type(kind: Function, returnType: "bool".toIntrinsic(), args: @[("", typeOfA, default), ("", self.inferOrError(branch.cond), default)])
|
||||
impl = self.match("==", fn, node)
|
||||
self.generateCall(impl, @[node.switch, branch.cond], impl.line)
|
||||
ifJump = self.emitJump(JumpIfFalsePop, branch.body.token.line)
|
||||
|
@ -1934,9 +1917,13 @@ proc varDecl(self: BytecodeCompiler, node: VarDecl) =
|
|||
if node.value.isNil():
|
||||
# Variable has no value: the type declaration
|
||||
# takes over
|
||||
if typ.kind == Auto:
|
||||
# TODO: Implement T.default()!
|
||||
if self.compare(typ, "auto".toIntrinsic()):
|
||||
self.error("automatic types require initialization", node)
|
||||
typ = self.inferOrError(node.valueType)
|
||||
# One of the few exceptions where we actually don't want to use
|
||||
# self.compare() is this one, because that will implicitly unwrap
|
||||
# the typevar and compare the wrapped type, which is not what we want
|
||||
if typ.kind != Typevar:
|
||||
self.error(&"expecting type name, got value of type {self.stringify(typ)} instead", node.name)
|
||||
elif node.valueType.isNil():
|
||||
|
@ -1948,7 +1935,7 @@ proc varDecl(self: BytecodeCompiler, node: VarDecl) =
|
|||
# a value: the value's type must match the
|
||||
# type declaration
|
||||
let expected = self.inferOrError(node.valueType)
|
||||
if expected.kind != Auto:
|
||||
if not self.compare(expected, "auto".toIntrinsic()):
|
||||
self.check(node.value, expected)
|
||||
# If this doesn't fail, then we're good
|
||||
typ = expected
|
||||
|
@ -1956,7 +1943,7 @@ proc varDecl(self: BytecodeCompiler, node: VarDecl) =
|
|||
# Let the compiler infer the type (this
|
||||
# is the default behavior already, but
|
||||
# some users may prefer to be explicit!)
|
||||
typ = self.inferOrError(node.value)
|
||||
typ = self.inferOrError(node.value)
|
||||
self.expression(node.value)
|
||||
self.emitByte(AddVar, node.token.line)
|
||||
inc(self.stackIndex)
|
||||
|
|
|
@ -6,6 +6,7 @@ fn sum(a, b: auto): auto {
|
|||
return a + b;
|
||||
}
|
||||
|
||||
|
||||
var x: auto = 1;
|
||||
print(x == 1);
|
||||
print(sum(1, 2) == 3);
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
import std;
|
||||
|
||||
# Note: unlike C-style casts, casting in peon tells the compiler
|
||||
# "hey, please reinterpret the underlying bits of data of this thing
|
||||
# as the type I'm telling you, trust me bro". There is no data conversion
|
||||
# occurring whatsoever! For that, use converters (once they're implemented LoL)
|
||||
|
||||
print(cast[int](2.0) == 4611686018427387904);
|
||||
print(cast[float](4611686018427387904) == 2.0);
|
||||
# If that strikes your fancy, you can do this:
|
||||
var x = int;
|
||||
var caster = cast[x];
|
||||
print(caster(2.0) == 4611686018427387904);
|
||||
|
|
|
@ -12,3 +12,4 @@ fn identity(x: int32): int32 {
|
|||
fn nope[T: int32 | int16](x: T): T {
|
||||
return identity(x);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue