diff --git a/src/frontend/compiler/compiler.nim b/src/frontend/compiler/compiler.nim index 7ca6822..238f00b 100644 --- a/src/frontend/compiler/compiler.nim +++ b/src/frontend/compiler/compiler.nim @@ -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] diff --git a/src/frontend/compiler/targets/bytecode/target.nim b/src/frontend/compiler/targets/bytecode/target.nim index cc8f0a4..99f5e8e 100644 --- a/src/frontend/compiler/targets/bytecode/target.nim +++ b/src/frontend/compiler/targets/bytecode/target.nim @@ -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)) diff --git a/src/frontend/parsing/ast.nim b/src/frontend/parsing/ast.nim index f8c90a9..f911785 100644 --- a/src/frontend/parsing/ast.nim +++ b/src/frontend/parsing/ast.nim @@ -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})"""