diff --git a/src/frontend/compiler.nim b/src/frontend/compiler.nim index 96a6709..a9dccb3 100644 --- a/src/frontend/compiler.nim +++ b/src/frontend/compiler.nim @@ -40,7 +40,7 @@ type Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Float32, Float64, Char, Byte, String, Function, CustomType, - Nil, Nan, Bool, Inf, Typedesc, Generic, + Nil, Nan, Bool, Inf, Typevar, Generic, Mutable, Reference, Pointer Any # Any is used internally in a few cases, # for example when looking for operators @@ -57,6 +57,8 @@ type isCoroutine: bool args: seq[tuple[name: string, kind: Type]] returnType: Type + isBuiltinFunction: bool + builtinOp: string of Mutable, Reference, Pointer: value: Type else: @@ -96,9 +98,6 @@ type isFunctionArgument: bool # Where is this node declared in the file? line: int - # is this a builtin function? - isBuiltinFunction: bool - builtinOp: string Loop = object ## A "loop object" used ## by the compiler to emit @@ -181,8 +180,8 @@ proc varDecl(self: Compiler, node: VarDecl) proc inferType(self: Compiler, node: LiteralExpr): Type proc inferType(self: Compiler, node: Expression): Type proc findByName(self: Compiler, name: string): seq[Name] -proc findByType(self: Compiler, name: string, kind: Type): seq[Name] -proc compareTypes(self: Compiler, a, b: Type): bool +proc findByType(self: Compiler, name: string, kind: Type, strictRef: bool = true): seq[Name] +proc compareTypes(self: Compiler, a, b: Type, strictRef: bool = true): bool proc patchReturnAddress(self: Compiler, pos: int) proc handleMagicPragma(self: Compiler, pragma: Pragma, node: ASTnode) proc handlePurePragma(self: Compiler, pragma: Pragma, node: ASTnode) @@ -430,7 +429,7 @@ proc detectClosureVariable(self: Compiler, name: Name, depth: int = self.scopeDe name.isClosedOver = true -proc compareTypes(self: Compiler, a, b: Type): bool = +proc compareTypes(self: Compiler, a, b: Type, strictRef: bool = true): bool = ## Compares two type objects ## for equality (works with nil!) @@ -455,7 +454,17 @@ proc compareTypes(self: Compiler, a, b: Type): bool = # Next, we see the type discriminant: # If they're different, then they can't # be the same type! - return false + if strictRef: + return false + else: + # Well... unless we don't care about + # whether it's a ref/value/mutable type + if a.kind in {Reference, Pointer, Mutable}: + return self.compareTypes(a.value, b, strictRef) + elif b.kind in {Reference, Pointer, Mutable}: + return self.compareTypes(a, b.value, strictRef) + else: + return false case a.kind: # If all previous checks pass, it's time # to go through each possible type peon @@ -522,8 +531,8 @@ proc toIntrinsic(name: string): Type = return Type(kind: Inf) elif name == "bool": return Type(kind: Bool) - elif name == "type": - return Type(kind: Typedesc) + elif name == "typevar": + return Type(kind: Typevar) else: return nil @@ -765,11 +774,11 @@ proc findByName(self: Compiler, name: string): seq[Name] = result.add(obj) -proc findByType(self: Compiler, name: string, kind: Type): seq[Name] = +proc findByType(self: Compiler, name: string, kind: Type, strictRef: bool = true): seq[Name] = ## Looks for objects that have already been declared ## with the given name and type for obj in self.findByName(name): - if self.compareTypes(obj.valueType, kind): + if self.compareTypes(obj.valueType, kind, strictRef): result.add(obj) @@ -777,7 +786,7 @@ proc matchImpl(self: Compiler, name: string, kind: Type): Name = ## Tries to find a matching function implementation ## compatible with the given type and returns its ## name object - let impl = self.findByType(name, kind) + let impl = self.findByType(name, kind, strictRef=false) if impl.len() == 0: var msg = &"cannot find a suitable implementation for '{name}'" let names = self.findByName(name) @@ -818,10 +827,10 @@ proc emitFunction(self: Compiler, name: Name) = proc handleBuiltinFunction(self: Compiler, fn: Name, args: seq[Expression]) = ## Emits single instructions for builtin functions ## such as addition or subtraction - if fn.builtinOp notin ["GenericLogicalOr", "GenericLogicalAnd"]: + if fn.valueType.builtinOp notin ["GenericLogicalOr", "GenericLogicalAnd"]: for argument in args: self.expression(argument) - case fn.builtinOp: + case fn.valueType.builtinOp: of "AddInt64": self.emitByte(AddInt64) of "SubInt64": @@ -924,7 +933,7 @@ proc handleBuiltinFunction(self: Compiler, fn: Name, args: seq[Expression]) = proc generateCall(self: Compiler, fn: Name, args: seq[Expression]) = ## Small wrapper that abstracts emitting a call instruction ## for a given function - if fn.isBuiltinFunction: + if fn.valueType.isBuiltinFunction: self.handleBuiltinFunction(fn, args) return self.emitFunction(fn) @@ -1021,13 +1030,14 @@ proc declareName(self: Compiler, node: Declaration, mutable: bool = false) = isPrivate: node.isPrivate, owner: self.currentModule, isConst: node.isConst, - valueType: Type(kind: self.inferType(node.value).kind), + valueType: self.inferType(node.value), codePos: self.chunk.code.len(), isLet: node.isLet, isClosedOver: false, line: node.token.line)) - if mutable: - self.names[^1].valueType = Type(kind: Mutable, value: self.names[^1].valueType) + # TODO: + # if mutable: + # self.names[^1].valueType = Type(kind: Mutable, value: self.names[^1].valueType) # We emit a jump of 0 because this may become a # StoreHeap instruction. If they variable is # not closed over, we'll sadly be wasting a @@ -1116,8 +1126,11 @@ proc identifier(self: Compiler, node: IdentExpr) = else: self.detectClosureVariable(s) if s.valueType.kind == Function: - self.emitByte(LoadFunctionObj) - self.emitBytes(s.codePos.toTriple()) + if not s.valueType.isBuiltinFunction: + self.emitByte(LoadFunctionObj) + self.emitBytes(s.codePos.toTriple()) + else: + self.emitByte(LoadNil) elif not s.isClosedOver: # Static name resolution, loads value at index in the stack. Very fast. Much wow. self.emitByte(LoadVar) @@ -1352,12 +1365,12 @@ proc callExpr(self: Compiler, node: CallExpr) = else: discard # TODO: Calling expressions if not funct.isNil(): - self.generateCall(funct, argExpr) - else: - if funct.isBuiltinFunction: + if funct.valueType.isBuiltinFunction: self.handleBuiltinFunction(funct, argExpr) else: - self.generateObjCall(argExpr) + self.generateCall(funct, argExpr) + else: + self.generateObjCall(argExpr) if self.scopeDepth > 0 and not self.checkCallIsPure(node.callee): if not self.currentFunction.name.isNil(): self.error(&"cannot make sure that calls to '{self.currentFunction.name.token.lexeme}' are side-effect free") @@ -1635,8 +1648,8 @@ proc handleMagicPragma(self: Compiler, pragma: Pragma, node: ASTNode) = self.error("'magic' pragma is not valid in this context") var node = FunDecl(node) var fn = self.resolve(node.name) - fn.isBuiltinFunction = true - fn.builtinOp = pragma.args[0].token.lexeme[1..^2] + fn.valueType.isBuiltinFunction = true + fn.valueType.builtinOp = pragma.args[0].token.lexeme[1..^2] proc handlePurePragma(self: Compiler, pragma: Pragma, node: ASTNode) = @@ -1673,7 +1686,7 @@ proc funDecl(self: Compiler, node: FunDecl) = self.dispatchPragmas(node) let fn = self.names[^(node.arguments.len() + 1)] var jmp: int - if not fn.isBuiltinFunction: + if not fn.valueType.isBuiltinFunction: self.frames.add(self.names.high()) # A function's code is just compiled linearly # and then jumped over @@ -1699,7 +1712,7 @@ proc funDecl(self: Compiler, node: FunDecl) = self.error(&"cannot infer the type of '{node.returnType.token.lexeme}'") # TODO: Forward declarations if not node.body.isNil(): - if BlockStmt(node.body).code.len() == 0 and not fn.isBuiltinFunction: + if BlockStmt(node.body).code.len() == 0 and not fn.valueType.isBuiltinFunction: self.error("cannot declare function with empty body") let fnType = self.inferType(node) let impl = self.findByType(node.name.token.lexeme, fnType) @@ -1713,7 +1726,7 @@ proc funDecl(self: Compiler, node: FunDecl) = # We store the current function self.currentFunction = node - if not fn.isBuiltinFunction: + if not fn.valueType.isBuiltinFunction: # Since the deferred array is a linear # sequence of instructions and we want # to keep track to whose function's each diff --git a/tests/functionObj.pn b/tests/functionObj.pn index 0b56322..7e1732f 100644 --- a/tests/functionObj.pn +++ b/tests/functionObj.pn @@ -6,12 +6,11 @@ fn getFunction: fn (n: int): int { } -getFunction()(5); - - operator `+`(a, b: int): int { #pragma[magic: "AddInt64"] } -`+`(1, 2); \ No newline at end of file +getFunction()(5); +var x = `+`; +x(1, 2); \ No newline at end of file