Fixed issue with self-referencing variable declarations

This commit is contained in:
Mattia Giambirtone 2023-01-22 17:58:32 +01:00
parent ace04ee34c
commit d1d5c10e49
3 changed files with 28 additions and 13 deletions

View File

@ -616,7 +616,7 @@ method infer*(self: Compiler, node: LiteralExpr): Type =
discard # Unreachable
method infer*(self: Compiler, node: Expression): Type =
method infer*(self: Compiler, node: Expression): Type =
## Infers the type of a given expression and
## returns it
if node.isNil():
@ -1008,7 +1008,8 @@ proc declare*(self: Compiler, node: ASTNode): Name {.discardable.} =
line: node.token.line,
isPrivate: node.isPrivate,
isReal: true,
belongsTo: self.currentFunction
belongsTo: self.currentFunction,
valueType: Type(kind: CustomType)
)
)
n = self.names[^1]

View File

@ -94,7 +94,7 @@ proc compile*(self: BytecodeCompiler, ast: seq[Declaration], file: string, lines
mode: CompileMode = Debug): Chunk
proc statement(self: BytecodeCompiler, node: Statement)
proc declaration(self: BytecodeCompiler, node: Declaration)
proc varDecl(self: BytecodeCompiler, node: VarDecl, name: Name)
proc varDecl(self: BytecodeCompiler, node: VarDecl)
proc specialize(self: BytecodeCompiler, typ: Type, args: seq[Expression]): Type {.discardable.}
proc patchReturnAddress(self: BytecodeCompiler, pos: int)
proc handleMagicPragma(self: BytecodeCompiler, pragma: Pragma, name: Name)
@ -1194,19 +1194,22 @@ method identifier(self: BytecodeCompiler, node: IdentExpr, name: Name = nil, com
return nil
result = s.valueType
if not compile:
return
return result
var node = s.ident
if s.isConst:
# Constants are always emitted as Load* instructions
# no matter the scope depth
self.emitConstant(VarDecl(s.node).value, self.infer(node))
if strict:
self.emitConstant(VarDecl(s.node).value, self.inferOrError(node))
else:
self.emitConstant(VarDecl(s.node).value, self.infer(node))
elif s.kind == NameKind.Function:
# Functions have no runtime representation, they're just
# a location to jump to, but we pretend they aren't and
# resolve them to their address into our bytecode when
# they're referenced
self.emitByte(LoadUInt64, node.token.line)
self.emitBytes(self.chunk.writeConstant(s.valueType.location.toLong()), node.token.line)
self.emitBytes(self.chunk.writeConstant(s.codePos.toLong()), node.token.line)
elif s.isBuiltin:
case s.ident.token.lexeme:
of "nil":
@ -1782,7 +1785,7 @@ proc statement(self: BytecodeCompiler, node: Statement) =
self.expression(Expression(node))
proc varDecl(self: BytecodeCompiler, node: VarDecl, name: Name) =
proc varDecl(self: BytecodeCompiler, node: VarDecl) =
## Compiles variable declarations
var typ: Type
# Our parser guarantees that the variable declaration
@ -1793,7 +1796,7 @@ proc varDecl(self: BytecodeCompiler, node: VarDecl, name: Name) =
typ = self.inferOrError(node.valueType)
if typ.kind == Auto:
self.error("automatic types require initialization", node)
elif node.valueType.isNil:
elif node.valueType.isNil():
# Variable has no type declaration: the type
# of its value takes over
typ = self.inferOrError(node.value)
@ -1811,6 +1814,12 @@ proc varDecl(self: BytecodeCompiler, node: VarDecl, name: Name) =
self.expression(node.value)
self.emitByte(AddVar, node.token.line)
inc(self.stackIndex)
# We declare the name only now in order to make
# sure that stuff like var n = n; works as expected.
# If we declared it early, we'd have a duplicate with
# no type that would shadow the original value, which
# is no good
var name = self.declare(node)
name.position = self.stackIndex
name.valueType = typ
@ -1909,6 +1918,13 @@ proc funDecl(self: BytecodeCompiler, node: FunDecl, name: Name) =
self.stackIndex = stackIdx
proc typeDecl(self: BytecodeCompiler, node: TypeDecl, name: Name) =
## Compiles type declarations
for field in node.fields:
if self.compare(self.inferOrError(field.valueType), name.valueType) and not node.isRef:
self.error(&"illegal type recursion for non-ref type '{name.ident.token.lexeme}'")
proc declaration(self: BytecodeCompiler, node: Declaration) =
## Compiles declarations, statements and expressions
## recursively
@ -1941,11 +1957,9 @@ proc declaration(self: BytecodeCompiler, node: Declaration) =
if not name.valueType.returnType.isNil() and name.valueType.returnType.kind == Generic:
name.valueType.returnType.asUnion = true
of NodeKind.typeDecl:
# Custom types don't do much other than
# declaring a name in the given scope
self.declare(node)
self.typeDecl(TypeDecl(node), self.declare(node))
of NodeKind.varDecl:
self.varDecl(VarDecl(node), self.declare(node))
self.varDecl(VarDecl(node))
else:
self.statement(Statement(node))

View File

@ -757,7 +757,7 @@ proc `$`*(self: ASTNode): string =
result &= &"""FunDecl(name={self.name}, body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generics=[{self.generics.join(", ")}], async={self.isAsync}, generator={self.isGenerator}, template={self.isTemplate}, private={self.isPrivate}, pragmas={self.pragmas})"""
of typeDecl:
var self = TypeDecl(self)
result &= &"""TypeDecl(name={self.name}, fields={self.fields}, defaults={self.defaults}, private={self.isPrivate}, pragmas={self.pragmas}, generics={self.generics}, parent={self.parent}, ref={self.isRef}, enum={self.isEnum})"""
result &= &"""TypeDecl(name={self.name}, fields={self.fields}, defaults={self.defaults}, private={self.isPrivate}, pragmas={self.pragmas}, generics={self.generics}, parent={self.parent}, ref={self.isRef}, enum={self.isEnum}, value={self.value})"""
of lambdaExpr:
var self = LambdaExpr(self)
result &= &"""Lambda(body={self.body}, type={self.returnType}, arguments=[{self.arguments.join(", ")}], defaults=[{self.defaults.join(", ")}], generator={self.isGenerator}, async={self.isAsync}, pragmas={self.pragmas})"""