More fixes for assigning builtin functions to variables

This commit is contained in:
Mattia Giambirtone 2022-06-14 23:34:42 +02:00
parent b974ba8ba3
commit 6f60f76270
2 changed files with 46 additions and 34 deletions

View File

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

View File

@ -6,12 +6,11 @@ fn getFunction: fn (n: int): int {
} }
getFunction()(5);
operator `+`(a, b: int): int { operator `+`(a, b: int): int {
#pragma[magic: "AddInt64"] #pragma[magic: "AddInt64"]
} }
`+`(1, 2); getFunction()(5);
var x = `+`;
x(1, 2);