From 8667cbdceb90bd4bdf8be346f03208f89ae2ef45 Mon Sep 17 00:00:00 2001 From: Mattia Giambirtone Date: Thu, 13 Oct 2022 16:52:37 +0200 Subject: [PATCH] Initial ground work on generics, some stuff is broken :( --- src/frontend/compiler.nim | 181 ++++++++++----- tests/generics.pn | 6 +- tests/std.pn | 452 ++------------------------------------ 3 files changed, 155 insertions(+), 484 deletions(-) diff --git a/src/frontend/compiler.nim b/src/frontend/compiler.nim index 658d2ac..27b3723 100644 --- a/src/frontend/compiler.nim +++ b/src/frontend/compiler.nim @@ -413,6 +413,7 @@ proc patchJump(self: Compiler, offset: int) = if jump < 0: self.error("invalid jump size (< 0), did the bytecode size change without fixJumps being called?") if jump > 16777215: + # TODO: Emit consecutive jumps? self.error("cannot jump more than 16777215 instructions") self.setJump(self.jumps[offset].offset, (jump - 4).toTriple()) self.jumps[offset].patched = true @@ -493,8 +494,43 @@ proc resolve(self: Compiler, name: IdentExpr, self.varDecl(VarDecl(obj.node), obj) of NameKind.Type: self.typeDecl(TypeDecl(obj.node), obj) + of NameKind.Function: + if not obj.valueType.isGeneric: + self.funDecl(FunDecl(obj.node), obj) + # Generic functions need to be compiled at + # the call site, but regular functions can + # be precompiled as soon as we resolve them else: - discard # Functions need to be compiled at the call site + discard + return obj + return nil + + +proc resolve(self: Compiler, name: string, + depth: int = self.scopeDepth): Name = + ## Version of resolve that takes strings instead + ## of AST nodes + for obj in reversed(self.names): + if obj.name.token.lexeme == name: + if obj.isPrivate and obj.owner != self.currentModule: + continue # There may be a name in the current module that + # matches, so we skip this + if not obj.resolved: + obj.resolved = true + obj.codePos = self.chunk.code.len() + case obj.kind: + of NameKind.Var: + self.varDecl(VarDecl(obj.node), obj) + of NameKind.Type: + self.typeDecl(TypeDecl(obj.node), obj) + of NameKind.Function: + if not obj.valueType.isGeneric: + self.funDecl(FunDecl(obj.node), obj) + # Generic functions need to be compiled at + # the call site, but regular functions can + # be precompiled as soon as we resolve them + else: + discard return obj return nil @@ -509,6 +545,8 @@ proc getStackPos(self: Compiler, name: Name): int = continue elif not variable.belongsTo.isNil() and variable.belongsTo.valueType.isBuiltinFunction: continue + elif not variable.valueType.isNil() and variable.valueType.kind == Generic: + continue if name == variable: found = true break @@ -722,11 +760,18 @@ proc infer(self: Compiler, node: Expression): Type = case node.kind: of identExpr: let node = IdentExpr(node) - let name = self.resolve(node) + var name = self.resolve(node) if not name.isNil(): result = name.valueType - if result.kind == Generic: - result = self.resolve(newIdentExpr(Token(lexeme: result.name, kind: Identifier))).valueType + if not result.isNil() and result.kind == Generic: + if name.belongsTo.isNil(): + name = self.resolve(result.name) + if not name.isNil(): + return name.valueType + else: + for arg in name.belongsTo.valueType.args: + if node.token.lexeme == arg.name: + return arg.kind else: result = node.name.lexeme.toIntrinsic() of unaryExpr: @@ -886,7 +931,7 @@ proc check(self: Compiler, term: Expression, kind: Type, allowAny: bool = false) ## otherwise let k = self.infer(term) if k.isNil(): - if term.kind == identExpr: + if term.kind == identExpr and self.resolve(IdentExpr(term)).isNil(): self.error(&"reference to undeclared name '{term.token.lexeme}'", term) elif term.kind == callExpr and CallExpr(term).callee.kind == identExpr: self.error(&"call to undeclared function '{CallExpr(term).callee.token.lexeme}'", term) @@ -1011,7 +1056,10 @@ proc endScope(self: Compiler) = for name in self.names: if name.depth > self.scopeDepth: names.add(name) - if name.kind in [NameKind.Var, NameKind.Argument]: + if not name.resolved: + # TODO: Emit a warning? + continue + elif name.kind in [NameKind.Var, NameKind.Argument]: # We don't increase the pop count for some kinds of objects # because they're not stored the same way as regular variables # (for types, generics and function declarations) @@ -1211,6 +1259,7 @@ proc emitLoop(self: Compiler, begin: int, line: int) = ## jump offset let offset = self.chunk.code.high() - begin + 4 if offset > 16777215: + # TODO: Emit consecutive jumps? self.error("cannot jump more than 16777215 bytecode instructions") self.emitByte(JumpBackwards, line) self.emitBytes(offset.toTriple(), line) @@ -1625,31 +1674,40 @@ proc generateCall(self: Compiler, fn: Name, args: seq[Expression], line: int) = self.patchReturnAddress(pos) -proc shadowGenerics(self: Compiler, fn: Name, args: seq[Expression]) = +proc specializeGeneric(self: Compiler, fn: Name, args: seq[Expression]): Name = ## Specializes a generic function by - ## shadowing the names of the generic - ## types with their respective concrete - ## types + ## instantiating a concrete version + ## of it var mapping: TableRef[string, Type] = newTable[string, Type]() var kind: Type + result = deepCopy(fn) # This first loop checks if a user tries to reassign a generic's # name to a different type - for i, (name, typ) in fn.valueType.args: + for i, (name, typ) in result.valueType.args: if typ.kind != Generic: continue kind = self.infer(args[i]) if typ.name in mapping and not self.compare(kind, mapping[typ.name]): self.error(&"expected generic argument '{typ.name}' to be of type {self.typeToStr(mapping[typ.name])}, got {self.typeToStr(kind)} instead") mapping[typ.name] = kind - for gen in mapping.keys(): - kind = mapping[gen] + result.valueType.args[i].kind = kind + for (argExpr, argName) in zip(args, result.valueType.args): + if self.names.high() > 16777215: + self.error("cannot declare more than 16777215 variables at a time") self.names.add(Name(depth: self.scopeDepth + 1, isPrivate: true, - isConst: false, owner: self.currentModule, + isConst: false, + name: newIdentExpr(Token(lexeme: argName.name)), + valueType: argName.kind, + codePos: 0, + isLet: false, line: fn.line, - valueType: kind, - name: newIdentExpr(Token(kind: TokenType.Identifier, lexeme: gen)))) + belongsTo: result, + kind: NameKind.Argument + )) + if result.valueType.returnType.kind == Generic: + result.valueType.returnType = mapping[result.valueType.returnType.name] proc callExpr(self: Compiler, node: CallExpr): Name {.discardable.} = @@ -1663,7 +1721,7 @@ proc callExpr(self: Compiler, node: CallExpr): Name {.discardable.} = dec(i) kind = self.infer(argument) if kind.isNil(): - if argument.kind == identExpr: + if argument.kind == identExpr and self.resolve(IdentExpr(argument)).isNil(): self.error(&"reference to undeclared name '{IdentExpr(argument).name.lexeme}'") if node.callee.kind != identExpr: self.error(&"cannot infer the type of argument {i + 1} in call") @@ -1676,9 +1734,13 @@ proc callExpr(self: Compiler, node: CallExpr): Name {.discardable.} = # Calls like hi() result = self.matchImpl(IdentExpr(node.callee).name.lexeme, Type(kind: Function, returnType: Type(kind: Any), args: args), node) if result.valueType.isGeneric: - self.shadowGenerics(result, argExpr) - # Here is when the function gets *actually* compiled - self.funDecl(FunDecl(result.node), result) + result = self.specializeGeneric(result, argExpr) + # We can't instantiate a concrete version + # of a generic function without the types + # of its arguments, so we wait until the + # very last moment to compile it, once + # that info is available to us + self.funDecl(FunDecl(result.node), result) # Now we call it self.generateCall(result, argExpr, node.token.line) of NodeKind.callExpr: @@ -1849,6 +1911,44 @@ proc importStmt(self: Compiler, node: ImportStmt) = self.error(&"""could not import '{filename}': {getCurrentExceptionMsg()} [errno {osLastError()}]""") +proc printRepl(self: Compiler, typ: Type, node: Expression) = + ## Emits instruction to print + ## peon types in REPL mode + case typ.kind: + of Generic: + discard # TODO + of Int64: + self.emitByte(PrintInt64, node.token.line) + of UInt64: + self.emitByte(PrintUInt64, node.token.line) + of Int32: + self.emitByte(PrintInt32, node.token.line) + of UInt32: + self.emitByte(PrintInt32, node.token.line) + of Int16: + self.emitByte(PrintInt16, node.token.line) + of UInt16: + self.emitByte(PrintUInt16, node.token.line) + of Int8: + self.emitByte(PrintInt8, node.token.line) + of UInt8: + self.emitByte(PrintUInt8, node.token.line) + of Float64: + self.emitByte(PrintFloat64, node.token.line) + of Float32: + self.emitByte(PrintFloat32, node.token.line) + of Bool: + self.emitByte(PrintBool, node.token.line) + of Nan: + self.emitByte(PrintNan, node.token.line) + of Inf: + self.emitByte(PrintInf, node.token.line) + of String: + self.emitByte(PrintString, node.token.line) + else: + self.emitByte(PrintHex, node.token.line) + + proc statement(self: Compiler, node: Statement) = ## Compiles all statements case node.kind: @@ -1861,37 +1961,7 @@ proc statement(self: Compiler, node: Statement) = # so we don't have to pop anything discard elif self.replMode: - case kind.kind: - of Int64: - self.emitByte(PrintInt64, node.token.line) - of UInt64: - self.emitByte(PrintUInt64, node.token.line) - of Int32: - self.emitByte(PrintInt32, node.token.line) - of UInt32: - self.emitByte(PrintInt32, node.token.line) - of Int16: - self.emitByte(PrintInt16, node.token.line) - of UInt16: - self.emitByte(PrintUInt16, node.token.line) - of Int8: - self.emitByte(PrintInt8, node.token.line) - of UInt8: - self.emitByte(PrintUInt8, node.token.line) - of Float64: - self.emitByte(PrintFloat64, node.token.line) - of Float32: - self.emitByte(PrintFloat32, node.token.line) - of Bool: - self.emitByte(PrintBool, node.token.line) - of Nan: - self.emitByte(PrintNan, node.token.line) - of Inf: - self.emitByte(PrintInf, node.token.line) - of String: - self.emitByte(PrintString, node.token.line) - else: - self.emitByte(PrintHex, node.token.line) + self.printRepl(kind, expression) else: self.emitByte(Pop, node.token.line) of NodeKind.ifStmt: @@ -1942,13 +2012,20 @@ proc varDecl(self: Compiler, node: VarDecl, name: Name) = var name = node.value.token.lexeme if node.value.kind == callExpr: name = CallExpr(node.value).callee.token.lexeme - self.error(&"reference to undeclared name '{name}'") + if self.resolve(name).isNil(): + self.error(&"reference to undeclared name '{name}'") self.error(&"'{node.name.token.lexeme}' has no type") elif not expected.isNil() and expected.mutable: # I mean, variables *are* already mutable (some of them anyway) self.error(&"invalid type '{self.typeToStr(expected)}' for var") elif not self.compare(expected, actual): if not expected.isNil(): self.error(&"expected value of type '{self.typeToStr(expected)}', but '{node.name.token.lexeme}' is of type '{self.typeToStr(actual)}'") + if expected.isNil(): + name.valueType = actual + if actual.kind == Generic: + # We matched a generic type, but we + # need the concrete one + name.valueType = expected self.expression(node.value) self.emitByte(StoreVar, node.token.line) self.emitBytes(self.getStackPos(self.names[^1]).toTriple(), node.token.line) diff --git a/tests/generics.pn b/tests/generics.pn index a787ac2..e9c8ac9 100644 --- a/tests/generics.pn +++ b/tests/generics.pn @@ -6,6 +6,6 @@ fn sum[T: int | int32](a, b: T): T { } -# print(sum(1, 2)); -# print(sum(1'i32, 2'i32)); -print(sum(1'i16, 2'i16)); # Will not work if uncommented! \ No newline at end of file +print(sum(1, 2)); +print(sum(1'i32, 2'i32)); +# print(sum(1'i16, 2'i16)); # Will not work if uncommented! diff --git a/tests/std.pn b/tests/std.pn index 00539e6..5a3ee50 100644 --- a/tests/std.pn +++ b/tests/std.pn @@ -8,94 +8,22 @@ # - It makes the implementation easier and more flexible -# TODO: Use generics - -operator `+`*(a, b: int): int { +operator `+`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): T { #pragma[magic: "Add", pure] } -operator `+`*(a, b: uint64): uint64 { - #pragma[magic: "Add", pure] +operator `+`(a, b: float): float { + #pragma[magic: "AddFloat64", pure] } -operator `+`*(a, b: int32): int32 { - #pragma[magic: "Add", pure] +operator `+`(a, b: float32): float32 { + #pragma[magic: "AddFloat32", pure] } -operator `+`*(a, b: uint32): uint32 { - #pragma[magic: "Add", pure] -} - - -operator `+`*(a, b: int16): int16 { - #pragma[magic: "Add", pure] -} - - -operator `+`*(a, b: uint16): uint16 { - #pragma[magic: "Add", pure] -} - - -operator `+`*(a, b: int8): int8 { - #pragma[magic: "Add", pure] -} - - -operator `+`*(a, b: uint8): uint8 { - #pragma[magic: "Add", pure] -} - - -operator `+`*(a, b: float64): float64 { - #pragma[magic: "Add", pure] -} - - -operator `+`*(a, b: float32): float32 { - #pragma[magic: "Add", pure] -} - - -operator `-`*(a, b: int): int { - #pragma[magic: "Subtract", pure] -} - - -operator `-`*(a, b: uint64): uint64 { - #pragma[magic: "Subtract", pure] -} - - -operator `-`*(a, b: int32): int32 { - #pragma[magic: "Subtract", pure] -} - - -operator `-`*(a, b: uint32): uint32 { - #pragma[magic: "Subtract", pure] -} - - -operator `-`*(a, b: int16): int16 { - #pragma[magic: "Subtract", pure] -} - - -operator `-`*(a, b: uint16): uint16 { - #pragma[magic: "Subtract", pure] -} - - -operator `-`*(a, b: int8): int8 { - #pragma[magic: "Subtract", pure] -} - - -operator `-`*(a, b: uint8): uint8 { +operator `-`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): T { #pragma[magic: "Subtract", pure] } @@ -110,42 +38,7 @@ operator `-`*(a, b: float32): float32 { } -operator `*`*(a, b: int): int { - #pragma[magic: "Multiply", pure] -} - - -operator `*`*(a, b: uint64): uint64 { - #pragma[magic: "Multiply", pure] -} - - -operator `*`*(a, b: int32): int32 { - #pragma[magic: "Multiply", pure] -} - - -operator `*`*(a, b: uint32): uint32 { - #pragma[magic: "Multiply", pure] -} - - -operator `*`*(a, b: int16): int16 { - #pragma[magic: "Multiply", pure] -} - - -operator `*`*(a, b: uint16): uint16 { - #pragma[magic: "Multiply", pure] -} - - -operator `*`*(a, b: int8): int8 { - #pragma[magic: "Multiply", pure] -} - - -operator `*`*(a, b: uint8): uint8 { +operator `*`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): T { #pragma[magic: "Multiply", pure] } @@ -160,42 +53,12 @@ operator `*`*(a, b: float32): float32 { } -operator `/`*(a, b: int): int { +operator `/`*[T: int | int32 | int16 | int8](a, b: T): T { #pragma[magic: "SignedDivide", pure] } -operator `/`*(a, b: uint64): uint64 { - #pragma[magic: "Divide", pure] -} - - -operator `/`*(a, b: int32): int32 { - #pragma[magic: "SignedDivide", pure] -} - - -operator `/`*(a, b: uint32): uint32 { - #pragma[magic: "Divide", pure] -} - - -operator `/`*(a, b: int16): int16 { - #pragma[magic: "SignedDivide", pure] -} - - -operator `/`*(a, b: uint16): uint16 { - #pragma[magic: "Divide", pure] -} - - -operator `/`*(a, b: int8): int8 { - #pragma[magic: "SignedDivide", pure] -} - - -operator `/`*(a, b: uint8): uint8 { +operator `/`*[T: uint64 | uint32 | uint16 | uint8](a, b: T): T { #pragma[magic: "Divide", pure] } @@ -210,12 +73,12 @@ operator `/`*(a, b: float32): float32 { } -operator `**`*(a, b: int64): int64 { +operator `**`*[T: int | int32 | int16 | int8](a, b: T): T { #pragma[magic: "SignedPow", pure] } -operator `**`*(a, b: uint64): uint64 { +operator `**`*[T: uint64 | uint32 | uint16 | uint8](a, b: T): T { #pragma[magic: "Pow", pure] } @@ -226,301 +89,32 @@ operator `%`*(a, b: int64): int64 { # Comparison operators -operator `>`*(a, b: int): bool { +operator `>`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8 | float | float32](a, b: T): bool { #pragma[magic: "GreaterThan", pure] } -operator `<`*(a, b: int): bool { +operator `<`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8 | float | float32](a, b: T): bool { #pragma[magic: "LessThan", pure] } -operator `==`*(a, b: int): bool { +operator `==`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8 | float | float32](a, b: T): bool { #pragma[magic: "Equal", pure] } -operator `!=`*(a, b: int): bool { +operator `!=`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8 | float | float32](a, b: T): bool { #pragma[magic: "NotEqual", pure] } -operator `>`*(a, b: uint64): bool { - #pragma[magic: "GreaterThan", pure] -} - - -operator `<`*(a, b: uint64): bool { - #pragma[magic: "LessThan", pure] -} - - -operator `==`*(a, b: uint64): bool { - #pragma[magic: "Equal", pure] -} - - -operator `!=`*(a, b: uint64): bool { - #pragma[magic: "NotEqual", pure] -} - - -operator `>`*(a, b: int32): bool { - #pragma[magic: "GreaterThan", pure] -} - - -operator `<`*(a, b: int32): bool { - #pragma[magic: "LessThan", pure] -} - - -operator `==`*(a, b: int32): bool { - #pragma[magic: "Equal", pure] -} - - -operator `!=`*(a, b: int32): bool { - #pragma[magic: "NotEqual", pure] -} - -operator `>`*(a, b: uint32): bool { - #pragma[magic: "GreaterThan", pure] -} - - -operator `<`*(a, b: uint32): bool { - #pragma[magic: "LessThan", pure] -} - - -operator `==`*(a, b: uint32): bool { - #pragma[magic: "Equal", pure] -} - - -operator `!=`*(a, b: uint32): bool { - #pragma[magic: "NotEqual", pure] -} - - -operator `>`*(a, b: int16): bool { - #pragma[magic: "GreaterThan", pure] -} - - -operator `<`*(a, b: int16): bool { - #pragma[magic: "LessThan", pure] -} - - -operator `==`*(a, b: int16): bool { - #pragma[magic: "Equal", pure] -} - - -operator `!=`*(a, b: int16): bool { - #pragma[magic: "NotEqual", pure] -} - - -operator `>`*(a, b: uint16): bool { - #pragma[magic: "GreaterThan", pure] -} - - -operator `<`*(a, b: uint16): bool { - #pragma[magic: "LessThan", pure] -} - - -operator `==`*(a, b: uint16): bool { - #pragma[magic: "Equal", pure] -} - - -operator `!=`*(a, b: uint16): bool { - #pragma[magic: "NotEqual", pure] -} - - -operator `>`*(a, b: int8): bool { - #pragma[magic: "GreaterThan", pure] -} - - -operator `<`*(a, b: int8): bool { - #pragma[magic: "LessThan", pure] -} - - -operator `==`*(a, b: int8): bool { - #pragma[magic: "Equal", pure] -} - - -operator `!=`*(a, b: int8): bool { - #pragma[magic: "NotEqual", pure] -} - - -operator `>`*(a, b: uint8): bool { - #pragma[magic: "GreaterThan", pure] -} - - -operator `<`*(a, b: uint8): bool { - #pragma[magic: "LessThan", pure] -} - - -operator `==`*(a, b: uint8): bool { - #pragma[magic: "Equal", pure] -} - - -operator `!=`*(a, b: uint8): bool { - #pragma[magic: "NotEqual", pure] -} - - -operator `>`*(a, b: float): bool { - #pragma[magic: "GreaterThan", pure] -} - - -operator `<`*(a, b: float): bool { - #pragma[magic: "LessThan", pure] -} - - -operator `==`*(a, b: float): bool { - #pragma[magic: "Equal", pure] -} - - -operator `!=`*(a, b: float): bool { - #pragma[magic: "NotEqual", pure] -} - - -operator `>`*(a, b: float32): bool { - #pragma[magic: "GreaterThan", pure] -} - - -operator `<`*(a, b: float32): bool { - #pragma[magic: "LessThan", pure] -} - - -operator `==`*(a, b: float32): bool { - #pragma[magic: "Equal", pure] -} - - -operator `!=`*(a, b: float32): bool { - #pragma[magic: "NotEqual", pure] -} - - -operator `>=`*(a, b: int): bool { +operator `>=`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8 | float | float32](a, b: T): bool { #pragma[magic: "GreaterOrEqual", pure] } -operator `<=`*(a, b: int): bool { - #pragma[magic: "LessOrEqual", pure] -} - - -operator `>=`*(a, b: uint64): bool { - #pragma[magic: "GreaterOrEqual", pure] -} - - -operator `<=`*(a, b: uint64): bool { - #pragma[magic: "LessOrEqual", pure] -} - - -operator `>=`*(a, b: int32): bool { - #pragma[magic: "GreaterOrEqual", pure] -} - - -operator `<=`*(a, b: int32): bool { - #pragma[magic: "LessOrEqual", pure] -} - - -operator `>=`*(a, b: uint32): bool { - #pragma[magic: "GreaterOrEqual", pure] -} - - -operator `<=`*(a, b: uint32): bool { - #pragma[magic: "LessOrEqual", pure] -} - - -operator `>=`*(a, b: int16): bool { - #pragma[magic: "GreaterOrEqual", pure] -} - - -operator `<=`*(a, b: int16): bool { - #pragma[magic: "LessOrEqual", pure] -} - - -operator `>=`*(a, b: uint16): bool { - #pragma[magic: "GreaterOrEqual", pure] -} - - -operator `<=`*(a, b: uint16): bool { - #pragma[magic: "LessOrEqual", pure] -} - - -operator `>=`*(a, b: int8): bool { - #pragma[magic: "GreaterOrEqual", pure] -} - - -operator `<=`*(a, b: int8): bool { - #pragma[magic: "LessOrEqual", pure] -} - - -operator `>=`*(a, b: uint8): bool { - #pragma[magic: "GreaterOrEqual", pure] -} - - -operator `<=`*(a, b: uint8): bool { - #pragma[magic: "LessOrEqual", pure] -} - - -operator `>=`*(a, b: float): bool { - #pragma[magic: "GreaterOrEqual", pure] -} - - -operator `<=`*(a, b: float): bool { - #pragma[magic: "LessOrEqual", pure] -} - - -operator `>=`*(a, b: float32): bool { - #pragma[magic: "GreaterOrEqual", pure] -} - - -operator `<=`*(a, b: float32): bool { +operator `<=`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8 | float | float32](a, b: T): bool { #pragma[magic: "LessOrEqual", pure] } @@ -545,34 +139,34 @@ operator `&`*(a, b: int): bool { } -operator `|`*(a, b: int): int { +operator `|`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): int { #pragma[magic: "Or", pure] } -operator `~`*(a: int): int { +operator `~`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a: T): T { #pragma[magic: "Not", pure] } -operator `>>`*(a, b: int): int { +operator `>>`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): T { #pragma[magic: "RShift", pure] } -operator `<<`*(a, b: int): int { +operator `<<`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): T { #pragma[magic: "LShift", pure] } -operator `^`*(a, b: int): int { +operator `^`*[T: int | uint64 | int32 | uint32 | int16 | uint16 | int8 | uint8](a, b: T): T { #pragma[magic: "Xor", pure] } # Assignment operators -operator `=`*[T: all](a: var T, b: T) { +operator `=`*[T: all](a: var T, b: T) { # TODO: This is just a placeholder right now #pragma[magic: "GenericAssign"] }