mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
rework the vtable implementation embedding the vtable array directly with new strictions on methods (#22991)
**TODO** - [x] fixes changelog With the new option `nimPreviewVtables`, `methods` are confined in the same module where the type of the first parameter is defined - [x] make it opt in after CI checks its feasibility ## In the following-up PRs - [ ] in the following PRs, refactor code into a more efficient one - [ ] cpp needs special treatments since it cannot embed array in light of the preceding limits: ref https://github.com/nim-lang/Nim/pull/20977#discussion_r1035528927; we can support cpp backends with vtable implementations later on the comprise that uses indirect vtable access --------- Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
|
||||
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` was out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backwards compatibility.
|
||||
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
|
||||
- Methods now support implementations based on vtable by using `-d:nimPreviewVtables`. methods are confined in the same module where the type has been defined.
|
||||
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
import sighashes, modulegraphs, std/strscans
|
||||
import ../dist/checksums/src/checksums/md5
|
||||
import std/sequtils
|
||||
|
||||
type
|
||||
TypeDescKind = enum
|
||||
@@ -1718,6 +1719,13 @@ proc genTypeInfoV2OldImpl(m: BModule; t, origType: PType, name: Rope; info: TLin
|
||||
if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions:
|
||||
discard genTypeInfoV1(m, t, info)
|
||||
|
||||
proc genVTable(seqs: seq[PSym]): string =
|
||||
result = "{"
|
||||
for i in 0..<seqs.len:
|
||||
if i > 0: result.add ", "
|
||||
result.add "(void *) " & seqs[i].loc.r
|
||||
result.add "}"
|
||||
|
||||
proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) =
|
||||
cgsym(m, "TNimTypeV2")
|
||||
m.s[cfsStrData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name])
|
||||
@@ -1754,8 +1762,16 @@ proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineIn
|
||||
add(typeEntry, ", .traceImpl = (void*)")
|
||||
genHook(m, t, info, attachedTrace, typeEntry)
|
||||
|
||||
addf(typeEntry, ", .flags = $1};$n", [rope(flags)])
|
||||
m.s[cfsVars].add typeEntry
|
||||
let dispatchMethods = toSeq(getMethodsPerType(m.g.graph, t))
|
||||
if dispatchMethods.len > 0:
|
||||
addf(typeEntry, ", .flags = $1", [rope(flags)])
|
||||
for i in dispatchMethods:
|
||||
genProcPrototype(m, i)
|
||||
addf(typeEntry, ", .vTable = $1};$n", [genVTable(dispatchMethods)])
|
||||
m.s[cfsVars].add typeEntry
|
||||
else:
|
||||
addf(typeEntry, ", .flags = $1};$n", [rope(flags)])
|
||||
m.s[cfsVars].add typeEntry
|
||||
|
||||
if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions:
|
||||
discard genTypeInfoV1(m, t, info)
|
||||
|
||||
@@ -366,6 +366,8 @@ proc dataField(p: BProc): Rope =
|
||||
else:
|
||||
result = rope"->data"
|
||||
|
||||
proc genProcPrototype(m: BModule, sym: PSym)
|
||||
|
||||
include ccgliterals
|
||||
include ccgtypes
|
||||
|
||||
@@ -734,7 +736,7 @@ proc genVarPrototype(m: BModule, n: PNode)
|
||||
proc requestConstImpl(p: BProc, sym: PSym)
|
||||
proc genStmts(p: BProc, t: PNode)
|
||||
proc expr(p: BProc, n: PNode, d: var TLoc)
|
||||
proc genProcPrototype(m: BModule, sym: PSym)
|
||||
|
||||
proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc)
|
||||
proc intLiteral(i: BiggestInt; result: var Rope)
|
||||
proc genLiteral(p: BProc, n: PNode; result: var Rope)
|
||||
@@ -2181,9 +2183,8 @@ proc updateCachedModule(m: BModule) =
|
||||
cf.flags = {CfileFlag.Cached}
|
||||
addFileToCompile(m.config, cf)
|
||||
|
||||
proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode): PNode =
|
||||
proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
|
||||
## Also called from IC.
|
||||
result = nil
|
||||
if sfMainModule in m.module.flags:
|
||||
# phase ordering problem here: We need to announce this
|
||||
# dependency to 'nimTestErrorFlag' before system.c has been written to disk.
|
||||
@@ -2231,7 +2232,12 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode): PNode =
|
||||
|
||||
if m.g.forwardedProcs.len == 0:
|
||||
incl m.flags, objHasKidsValid
|
||||
result = generateMethodDispatchers(graph, m.idgen)
|
||||
if optMultiMethods in m.g.config.globalOptions or
|
||||
m.g.config.selectedGC notin {gcArc, gcOrc, gcAtomicArc} or
|
||||
not m.g.config.isDefined("nimPreviewVtables") or
|
||||
m.g.config.backend == backendCpp or sfCompileToCpp in m.module.flags:
|
||||
generateIfMethodDispatchers(graph, m.idgen)
|
||||
|
||||
|
||||
let mm = m
|
||||
m.g.modulesClosed.add mm
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
|
||||
import
|
||||
options, ast, msgs, idents, renderer, types, magicsys,
|
||||
sempass2, modulegraphs, lineinfos
|
||||
|
||||
sempass2, modulegraphs, lineinfos, astalgo
|
||||
|
||||
import std/intsets
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import std/[tables]
|
||||
|
||||
proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
|
||||
var dest = skipTypes(d, abstractPtrs)
|
||||
@@ -157,6 +157,9 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
|
||||
|
||||
proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
|
||||
var witness: PSym = nil
|
||||
if s.typ[1].owner.getModule != s.getModule and g.config.isDefined("nimPreviewVtables"):
|
||||
localError(g.config, s.info, errGenerated, "method `" & s.name.s &
|
||||
"` can be defined only in the same module with its type (" & s.typ[1].typeToString() & ")")
|
||||
for i in 0..<g.methods.len:
|
||||
let disp = g.methods[i].dispatcher
|
||||
case sameMethodBucket(disp, s, multimethods = optMultiMethods in g.config.globalOptions)
|
||||
@@ -175,6 +178,11 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
|
||||
of Invalid:
|
||||
if witness.isNil: witness = g.methods[i].methods[0]
|
||||
# create a new dispatcher:
|
||||
# stores the id and the position
|
||||
if s.typ[1].skipTypes(skipPtrs).itemId notin g.bucketTable:
|
||||
g.bucketTable[s.typ[1].skipTypes(skipPtrs).itemId] = 1
|
||||
else:
|
||||
g.bucketTable.inc(s.typ[1].skipTypes(skipPtrs).itemId)
|
||||
g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen)))
|
||||
#echo "adding ", s.info
|
||||
if witness != nil:
|
||||
@@ -183,7 +191,7 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
|
||||
elif sfBase notin s.flags:
|
||||
message(g.config, s.info, warnUseBase)
|
||||
|
||||
proc relevantCol(methods: seq[PSym], col: int): bool =
|
||||
proc relevantCol*(methods: seq[PSym], col: int): bool =
|
||||
# returns true iff the position is relevant
|
||||
result = false
|
||||
var t = methods[0].typ[col].skipTypes(skipPtrs)
|
||||
@@ -203,7 +211,7 @@ proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
|
||||
if (d != high(int)) and d != 0:
|
||||
return d
|
||||
|
||||
proc sortBucket(a: var seq[PSym], relevantCols: IntSet) =
|
||||
proc sortBucket*(a: var seq[PSym], relevantCols: IntSet) =
|
||||
# we use shellsort here; fast and simple
|
||||
var n = a.len
|
||||
var h = 1
|
||||
@@ -222,7 +230,7 @@ proc sortBucket(a: var seq[PSym], relevantCols: IntSet) =
|
||||
a[j] = v
|
||||
if h == 1: break
|
||||
|
||||
proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym =
|
||||
proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym =
|
||||
var base = methods[0].ast[dispatcherPos].sym
|
||||
result = base
|
||||
var paramLen = base.typ.len
|
||||
@@ -281,8 +289,7 @@ proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idg
|
||||
nilchecks.flags.incl nfTransf # should not be further transformed
|
||||
result.ast[bodyPos] = nilchecks
|
||||
|
||||
proc generateMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator): PNode =
|
||||
result = newNode(nkStmtList)
|
||||
proc generateIfMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator) =
|
||||
for bucket in 0..<g.methods.len:
|
||||
var relevantCols = initIntSet()
|
||||
for col in 1..<g.methods[bucket].methods[0].typ.len:
|
||||
@@ -291,4 +298,4 @@ proc generateMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator): PNode =
|
||||
# if multi-methods are not enabled, we are interested only in the first field
|
||||
break
|
||||
sortBucket(g.methods[bucket].methods, relevantCols)
|
||||
result.add newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols, idgen))
|
||||
g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, idgen)
|
||||
|
||||
@@ -50,10 +50,9 @@ proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var Alive
|
||||
let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p)
|
||||
cgen.genTopLevelStmt(bmod, n)
|
||||
|
||||
let disps = finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
|
||||
if disps != nil:
|
||||
for disp in disps:
|
||||
genProcAux(bmod, disp.sym)
|
||||
finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
|
||||
for disp in getDispatchers(g):
|
||||
genProcAux(bmod, disp)
|
||||
m.fromDisk.backendFlags = cgen.whichInitProcs(bmod)
|
||||
|
||||
proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) =
|
||||
|
||||
@@ -51,8 +51,10 @@ type
|
||||
typeInstCache*: seq[(PackedItemId, PackedItemId)]
|
||||
procInstCache*: seq[PackedInstantiation]
|
||||
attachedOps*: seq[(PackedItemId, TTypeAttachedOp, PackedItemId)]
|
||||
methodsPerType*: seq[(PackedItemId, int, PackedItemId)]
|
||||
methodsPerGenericType*: seq[(PackedItemId, int, PackedItemId)]
|
||||
enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
|
||||
methodsPerType*: seq[(PackedItemId, PackedItemId)]
|
||||
dispatchers*: seq[PackedItemId]
|
||||
|
||||
emittedTypeInfo*: seq[string]
|
||||
backendFlags*: set[ModuleBackendFlag]
|
||||
@@ -650,8 +652,10 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
|
||||
loadSeqSection typeInstCacheSection, m.typeInstCache
|
||||
loadSeqSection procInstCacheSection, m.procInstCache
|
||||
loadSeqSection attachedOpsSection, m.attachedOps
|
||||
loadSeqSection methodsPerTypeSection, m.methodsPerType
|
||||
loadSeqSection methodsPerGenericTypeSection, m.methodsPerGenericType
|
||||
loadSeqSection enumToStringProcsSection, m.enumToStringProcs
|
||||
loadSeqSection methodsPerTypeSection, m.methodsPerType
|
||||
loadSeqSection dispatchersSection, m.dispatchers
|
||||
loadSeqSection typeInfoSection, m.emittedTypeInfo
|
||||
|
||||
f.loadSection backendFlagsSection
|
||||
@@ -718,8 +722,10 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
|
||||
storeSeqSection typeInstCacheSection, m.typeInstCache
|
||||
storeSeqSection procInstCacheSection, m.procInstCache
|
||||
storeSeqSection attachedOpsSection, m.attachedOps
|
||||
storeSeqSection methodsPerTypeSection, m.methodsPerType
|
||||
storeSeqSection methodsPerGenericTypeSection, m.methodsPerGenericType
|
||||
storeSeqSection enumToStringProcsSection, m.enumToStringProcs
|
||||
storeSeqSection methodsPerTypeSection, m.methodsPerType
|
||||
storeSeqSection dispatchersSection, m.dispatchers
|
||||
storeSeqSection typeInfoSection, m.emittedTypeInfo
|
||||
|
||||
f.storeSection backendFlagsSection
|
||||
|
||||
@@ -135,8 +135,10 @@ proc checkModule(c: var CheckedContext; m: PackedModule) =
|
||||
typeInstCache*: seq[(PackedItemId, PackedItemId)]
|
||||
procInstCache*: seq[PackedInstantiation]
|
||||
attachedOps*: seq[(TTypeAttachedOp, PackedItemId, PackedItemId)]
|
||||
methodsPerType*: seq[(PackedItemId, int, PackedItemId)]
|
||||
methodsPerGenericType*: seq[(PackedItemId, int, PackedItemId)]
|
||||
enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
|
||||
methodsPerType*: seq[(PackedItemId, PackedItemId)]
|
||||
dispatchers*: seq[PackedItemId]
|
||||
]#
|
||||
|
||||
proc checkIntegrity*(g: ModuleGraph) =
|
||||
|
||||
@@ -103,6 +103,17 @@ proc replayBackendProcs*(g: ModuleGraph; module: int) =
|
||||
let symId = FullId(module: tmp.module, packed: it[1])
|
||||
g.enumToStringProcs[key] = LazySym(id: symId, sym: nil)
|
||||
|
||||
for it in mitems(g.packed[module].fromDisk.methodsPerType):
|
||||
let key = translateId(it[0], g.packed, module, g.config)
|
||||
let tmp = translateId(it[1], g.packed, module, g.config)
|
||||
let symId = FullId(module: tmp.module, packed: it[1])
|
||||
g.methodsPerType.mgetOrPut(key, @[]).add LazySym(id: symId, sym: nil)
|
||||
|
||||
for it in mitems(g.packed[module].fromDisk.dispatchers):
|
||||
let tmp = translateId(it, g.packed, module, g.config)
|
||||
let symId = FullId(module: tmp.module, packed: it)
|
||||
g.dispatchers.add LazySym(id: symId, sym: nil)
|
||||
|
||||
proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
|
||||
## We remember the generic instantiations a module performed
|
||||
## in order to to avoid the code bloat that generic code tends
|
||||
@@ -127,12 +138,12 @@ proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
|
||||
module: module, sym: FullId(module: sym.module, packed: it.sym),
|
||||
concreteTypes: concreteTypes, inst: nil)
|
||||
|
||||
for it in mitems(g.packed[module].fromDisk.methodsPerType):
|
||||
for it in mitems(g.packed[module].fromDisk.methodsPerGenericType):
|
||||
let key = translateId(it[0], g.packed, module, g.config)
|
||||
let col = it[1]
|
||||
let tmp = translateId(it[2], g.packed, module, g.config)
|
||||
let symId = FullId(module: tmp.module, packed: it[2])
|
||||
g.methodsPerType.mgetOrPut(key, @[]).add (col, LazySym(id: symId, sym: nil))
|
||||
g.methodsPerGenericType.mgetOrPut(key, @[]).add (col, LazySym(id: symId, sym: nil))
|
||||
|
||||
replayBackendProcs(g, module)
|
||||
|
||||
|
||||
@@ -92,8 +92,10 @@ type
|
||||
typeInstCacheSection
|
||||
procInstCacheSection
|
||||
attachedOpsSection
|
||||
methodsPerTypeSection
|
||||
methodsPerGenericTypeSection
|
||||
enumToStringProcsSection
|
||||
methodsPerTypeSection
|
||||
dispatchersSection
|
||||
typeInfoSection # required by the backend
|
||||
backendFlagsSection
|
||||
aliveSymsSection # beware, this is stored in a `.alivesyms` file.
|
||||
|
||||
@@ -3097,9 +3097,8 @@ proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
|
||||
var p = newInitProc(globals, m)
|
||||
attachProc(p, prc)
|
||||
|
||||
var disp = generateMethodDispatchers(graph, m.idgen)
|
||||
for i in 0..<disp.len:
|
||||
let prc = disp[i].sym
|
||||
generateIfMethodDispatchers(graph, m.idgen)
|
||||
for prc in getDispatchers(graph):
|
||||
if not globals.generatedSyms.containsOrIncl(prc.id):
|
||||
var p = newInitProc(globals, m)
|
||||
attachProc(p, prc)
|
||||
|
||||
@@ -16,6 +16,7 @@ import ../dist/checksums/src/checksums/md5
|
||||
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages
|
||||
import ic / [packed_ast, ic]
|
||||
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
@@ -81,7 +82,7 @@ type
|
||||
typeInstCache*: Table[ItemId, seq[LazyType]] # A symbol's ItemId.
|
||||
procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId.
|
||||
attachedOps*: array[TTypeAttachedOp, Table[ItemId, LazySym]] # Type ID, destructors, etc.
|
||||
methodsPerType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
|
||||
methodsPerGenericType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
|
||||
memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual,member and ctor so far).
|
||||
initializersPerType*: Table[ItemId, PNode] # Type ID, AST call to the default ctor (c++ only)
|
||||
enumToStringProcs*: Table[ItemId, LazySym]
|
||||
@@ -110,6 +111,11 @@ type
|
||||
suggestSymbols*: Table[FileIndex, seq[SymInfoPair]]
|
||||
suggestErrors*: Table[FileIndex, seq[Suggest]]
|
||||
methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization!
|
||||
bucketTable*: CountTable[ItemId]
|
||||
objectTree*: Table[ItemId, seq[tuple[depth: int, value: PType]]]
|
||||
methodsPerType*: Table[ItemId, seq[LazySym]]
|
||||
dispatchers*: seq[LazySym]
|
||||
|
||||
systemModule*: PSym
|
||||
sysTypes*: array[TTypeKind, PType]
|
||||
compilerprocs*: TStrTable
|
||||
@@ -153,8 +159,10 @@ proc resetForBackend*(g: ModuleGraph) =
|
||||
g.procInstCache.clear()
|
||||
for a in mitems(g.attachedOps):
|
||||
a.clear()
|
||||
g.methodsPerType.clear()
|
||||
g.methodsPerGenericType.clear()
|
||||
g.enumToStringProcs.clear()
|
||||
g.dispatchers.setLen(0)
|
||||
g.methodsPerType.clear()
|
||||
|
||||
const
|
||||
cb64 = [
|
||||
@@ -325,6 +333,7 @@ iterator procInstCacheItems*(g: ModuleGraph; s: PSym): PInstantiation =
|
||||
for t in mitems(x[]):
|
||||
yield resolveInst(g, t)
|
||||
|
||||
|
||||
proc getAttachedOp*(g: ModuleGraph; t: PType; op: TTypeAttachedOp): PSym =
|
||||
## returns the requested attached operation for type `t`. Can return nil
|
||||
## if no such operation exists.
|
||||
@@ -348,6 +357,27 @@ proc completePartialOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttached
|
||||
toPackedGeneratedProcDef(value, g.encoders[module], g.packed[module].fromDisk)
|
||||
#storeAttachedProcDef(t, op, value, g.encoders[module], g.packed[module].fromDisk)
|
||||
|
||||
iterator getDispatchers*(g: ModuleGraph): PSym =
|
||||
for i in g.dispatchers.mitems:
|
||||
yield resolveSym(g, i)
|
||||
|
||||
proc addDispatchers*(g: ModuleGraph, value: PSym) =
|
||||
# TODO: add it for packed modules
|
||||
g.dispatchers.add LazySym(sym: value)
|
||||
|
||||
iterator resolveLazySymSeq(g: ModuleGraph, list: var seq[LazySym]): PSym =
|
||||
for it in list.mitems:
|
||||
yield resolveSym(g, it)
|
||||
|
||||
proc setMethodsPerType*(g: ModuleGraph; id: ItemId, methods: seq[LazySym]) =
|
||||
# TODO: add it for packed modules
|
||||
g.methodsPerType[id] = methods
|
||||
|
||||
iterator getMethodsPerType*(g: ModuleGraph; t: PType): PSym =
|
||||
if g.methodsPerType.contains(t.itemId):
|
||||
for it in mitems g.methodsPerType[t.itemId]:
|
||||
yield resolveSym(g, it)
|
||||
|
||||
proc getToStringProc*(g: ModuleGraph; t: PType): PSym =
|
||||
result = resolveSym(g, g.enumToStringProcs[t.itemId])
|
||||
assert result != nil
|
||||
@@ -356,12 +386,12 @@ proc setToStringProc*(g: ModuleGraph; t: PType; value: PSym) =
|
||||
g.enumToStringProcs[t.itemId] = LazySym(sym: value)
|
||||
|
||||
iterator methodsForGeneric*(g: ModuleGraph; t: PType): (int, PSym) =
|
||||
if g.methodsPerType.contains(t.itemId):
|
||||
for it in mitems g.methodsPerType[t.itemId]:
|
||||
if g.methodsPerGenericType.contains(t.itemId):
|
||||
for it in mitems g.methodsPerGenericType[t.itemId]:
|
||||
yield (it[0], resolveSym(g, it[1]))
|
||||
|
||||
proc addMethodToGeneric*(g: ModuleGraph; module: int; t: PType; col: int; m: PSym) =
|
||||
g.methodsPerType.mgetOrPut(t.itemId, @[]).add (col, LazySym(sym: m))
|
||||
g.methodsPerGenericType.mgetOrPut(t.itemId, @[]).add (col, LazySym(sym: m))
|
||||
|
||||
proc hasDisabledAsgn*(g: ModuleGraph; t: PType): bool =
|
||||
let op = getAttachedOp(g, t, attachedAsgn)
|
||||
|
||||
@@ -9,6 +9,7 @@ define:nimPreviewSlimSystem
|
||||
define:nimPreviewCstringConversion
|
||||
define:nimPreviewProcConversion
|
||||
define:nimPreviewRangeDefault
|
||||
define:nimPreviewVtables
|
||||
define:nimPreviewNonVarDestructor
|
||||
threads:off
|
||||
|
||||
|
||||
@@ -191,15 +191,16 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
|
||||
case graph.pipelinePass
|
||||
of CgenPass:
|
||||
if bModule != nil:
|
||||
let disps = finalCodegenActions(graph, BModule(bModule), finalNode)
|
||||
if disps != nil:
|
||||
let m = BModule(bModule)
|
||||
finalCodegenActions(graph, m, finalNode)
|
||||
if graph.dispatchers.len > 0:
|
||||
let ctx = preparePContext(graph, module, idgen)
|
||||
for disp in disps:
|
||||
let retTyp = disp.sym.typ[0]
|
||||
for disp in getDispatchers(graph):
|
||||
let retTyp = disp.typ[0]
|
||||
if retTyp != nil:
|
||||
# todo properly semcheck the code of dispatcher?
|
||||
createTypeBoundOps(graph, ctx, retTyp, disp.info, idgen)
|
||||
genProcAux(BModule(bModule), disp.sym)
|
||||
# TODO: properly semcheck the code of dispatcher?
|
||||
createTypeBoundOps(graph, ctx, retTyp, disp.ast.info, idgen)
|
||||
genProcAux(m, disp)
|
||||
discard closePContext(graph, ctx, nil)
|
||||
of JSgenPass:
|
||||
when not defined(leanCompiler):
|
||||
|
||||
@@ -20,6 +20,7 @@ import
|
||||
isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs,
|
||||
extccomp
|
||||
|
||||
import vtables
|
||||
import std/[strtabs, math, tables, intsets, strutils]
|
||||
|
||||
when not defined(leanCompiler):
|
||||
@@ -839,6 +840,15 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
|
||||
if c.config.cmd == cmdIdeTools:
|
||||
appendToModule(c.module, result)
|
||||
trackStmt(c, c.module, result, isTopLevel = true)
|
||||
if optMultiMethods notin c.config.globalOptions and
|
||||
c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
c.config.isDefined("nimPreviewVtables") and
|
||||
c.config.backend != backendCpp and
|
||||
sfCompileToCpp notin c.module.flags:
|
||||
sortVTableDispatchers(c.graph)
|
||||
|
||||
if sfMainModule in c.module.flags:
|
||||
collectVTableDispatchers(c.graph)
|
||||
|
||||
proc recoverContext(c: PContext) =
|
||||
# clean up in case of a semantic error: We clean up the stacks, etc. This is
|
||||
|
||||
@@ -24,6 +24,9 @@ when defined(useDfa):
|
||||
import liftdestructors
|
||||
include sinkparameter_inference
|
||||
|
||||
|
||||
import std/options as opt
|
||||
|
||||
#[ Second semantic checking pass over the AST. Necessary because the old
|
||||
way had some inherent problems. Performs:
|
||||
|
||||
@@ -91,6 +94,37 @@ const
|
||||
errXCannotBeAssignedTo = "'$1' cannot be assigned to"
|
||||
errLetNeedsInit = "'let' symbol requires an initialization"
|
||||
|
||||
proc getObjDepth(t: PType): Option[tuple[depth: int, root: ItemId]] =
|
||||
var x = t
|
||||
var res: tuple[depth: int, root: ItemId]
|
||||
res.depth = -1
|
||||
var stack = newSeq[ItemId]()
|
||||
while x != nil:
|
||||
x = skipTypes(x, skipPtrs)
|
||||
if x.kind != tyObject:
|
||||
return none(tuple[depth: int, root: ItemId])
|
||||
stack.add x.itemId
|
||||
x = x[0]
|
||||
inc(res.depth)
|
||||
res.root = stack[^2]
|
||||
result = some(res)
|
||||
|
||||
proc collectObjectTree(graph: ModuleGraph, n: PNode) =
|
||||
for section in n:
|
||||
if section.kind == nkTypeDef and section[^1].kind in {nkObjectTy, nkRefTy, nkPtrTy}:
|
||||
let typ = section[^1].typ.skipTypes(skipPtrs)
|
||||
if typ.len > 0 and typ[0] != nil:
|
||||
let depthItem = getObjDepth(typ)
|
||||
if isSome(depthItem):
|
||||
let (depthLevel, root) = depthItem.unsafeGet
|
||||
if depthLevel == 1:
|
||||
graph.objectTree[root] = @[]
|
||||
else:
|
||||
if root notin graph.objectTree:
|
||||
graph.objectTree[root] = @[(depthLevel, typ)]
|
||||
else:
|
||||
graph.objectTree[root].add (depthLevel, typ)
|
||||
|
||||
proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
|
||||
if typ == nil or sfGeneratedOp in tracked.owner.flags:
|
||||
# don't create type bound ops for anything in a function with a `nodestroy` pragma
|
||||
@@ -1323,8 +1357,11 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
of nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkLambda, nkFuncDef, nkDo:
|
||||
if n[0].kind == nkSym and n[0].sym.ast != nil:
|
||||
trackInnerProc(tracked, getBody(tracked.graph, n[0].sym))
|
||||
of nkTypeSection, nkMacroDef, nkTemplateDef:
|
||||
of nkMacroDef, nkTemplateDef:
|
||||
discard
|
||||
of nkTypeSection:
|
||||
if tracked.isTopLevel:
|
||||
collectObjectTree(tracked.graph, n)
|
||||
of nkCast:
|
||||
if n.len == 2:
|
||||
track(tracked, n[1])
|
||||
@@ -1637,13 +1674,18 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
checkNil(s, body, g.config, c.idgen)
|
||||
|
||||
proc trackStmt*(c: PContext; module: PSym; n: PNode, isTopLevel: bool) =
|
||||
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
|
||||
nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
|
||||
return
|
||||
let g = c.graph
|
||||
var effects = newNodeI(nkEffectList, n.info)
|
||||
var t: TEffects = initEffects(g, effects, module, c)
|
||||
t.isTopLevel = isTopLevel
|
||||
track(t, n)
|
||||
when defined(drnim):
|
||||
if c.graph.strongSemCheck != nil: c.graph.strongSemCheck(c.graph, module, n)
|
||||
case n.kind
|
||||
of {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
|
||||
nkConverterDef, nkMethodDef, nkIteratorDef}:
|
||||
discard
|
||||
of nkTypeSection:
|
||||
if isTopLevel:
|
||||
collectObjectTree(c.graph, n)
|
||||
else:
|
||||
let g = c.graph
|
||||
var effects = newNodeI(nkEffectList, n.info)
|
||||
var t: TEffects = initEffects(g, effects, module, c)
|
||||
t.isTopLevel = isTopLevel
|
||||
track(t, n)
|
||||
when defined(drnim):
|
||||
if c.graph.strongSemCheck != nil: c.graph.strongSemCheck(c.graph, module, n)
|
||||
|
||||
167
compiler/vtables.nim
Normal file
167
compiler/vtables.nim
Normal file
@@ -0,0 +1,167 @@
|
||||
import ast, modulegraphs, magicsys, lineinfos, options, cgmeth, types
|
||||
import std/[algorithm, tables, intsets, assertions]
|
||||
|
||||
|
||||
|
||||
proc genVTableDispatcher(g: ModuleGraph; methods: seq[PSym]; index: int): PSym =
|
||||
#[
|
||||
proc dispatch(x: Base, params: ...) =
|
||||
cast[proc bar(x: Base, params: ...)](x.vTable[index])(x, params)
|
||||
]#
|
||||
var base = methods[0].ast[dispatcherPos].sym
|
||||
result = base
|
||||
var paramLen = base.typ.len
|
||||
var body = newNodeI(nkStmtList, base.info)
|
||||
|
||||
var disp = newNodeI(nkIfStmt, base.info)
|
||||
|
||||
var vTableAccess = newNodeIT(nkBracketExpr, base.info, base.typ)
|
||||
let nimGetVTableSym = getCompilerProc(g, "nimGetVTable")
|
||||
let ptrPNimType = nimGetVTableSym.typ.n[1].sym.typ
|
||||
|
||||
var nTyp = base.typ.n[1].sym.typ
|
||||
var dispatchObject = newSymNode(base.typ.n[1].sym)
|
||||
if nTyp.kind == tyObject:
|
||||
dispatchObject = newTree(nkAddr, dispatchObject)
|
||||
else:
|
||||
if g.config.backend != backendCpp: # TODO: maybe handle ptr?
|
||||
if nTyp.kind == tyVar and nTyp.skipTypes({tyVar}).kind != tyObject:
|
||||
dispatchObject = newTree(nkDerefExpr, dispatchObject)
|
||||
|
||||
var getVTableCall = newTree(nkCall,
|
||||
newSymNode(nimGetVTableSym),
|
||||
dispatchObject,
|
||||
newIntNode(nkIntLit, index)
|
||||
)
|
||||
getVTableCall.typ = base.typ
|
||||
var vTableCall = newNodeIT(nkCall, base.info, base.typ[0])
|
||||
var castNode = newTree(nkCast,
|
||||
newNodeIT(nkType, base.info, base.typ),
|
||||
getVTableCall)
|
||||
|
||||
castNode.typ = base.typ
|
||||
vTableCall.add castNode
|
||||
for col in 1..<paramLen:
|
||||
let param = base.typ.n[col].sym
|
||||
vTableCall.add newSymNode(param)
|
||||
|
||||
var ret: PNode
|
||||
if base.typ[0] != nil:
|
||||
var a = newNodeI(nkFastAsgn, base.info)
|
||||
a.add newSymNode(base.ast[resultPos].sym)
|
||||
a.add vTableCall
|
||||
ret = newNodeI(nkReturnStmt, base.info)
|
||||
ret.add a
|
||||
else:
|
||||
ret = vTableCall
|
||||
|
||||
if base.typ.n[1].sym.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
|
||||
let ifBranch = newNodeI(nkElifBranch, base.info)
|
||||
let boolType = getSysType(g, unknownLineInfo, tyBool)
|
||||
var isNil = getSysMagic(g, unknownLineInfo, "isNil", mIsNil)
|
||||
let checkSelf = newNodeIT(nkCall, base.info, boolType)
|
||||
checkSelf.add newSymNode(isNil)
|
||||
checkSelf.add newSymNode(base.typ.n[1].sym)
|
||||
ifBranch.add checkSelf
|
||||
ifBranch.add newTree(nkCall,
|
||||
newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(base.typ.n[1].sym))
|
||||
let elseBranch = newTree(nkElifBranch, ret)
|
||||
disp.add ifBranch
|
||||
disp.add elseBranch
|
||||
else:
|
||||
disp = ret
|
||||
|
||||
body.add disp
|
||||
body.flags.incl nfTransf # should not be further transformed
|
||||
result.ast[bodyPos] = body
|
||||
|
||||
proc containGenerics(base: PType, s: seq[tuple[depth: int, value: PType]]): bool =
|
||||
result = tfHasMeta in base.flags
|
||||
for i in s:
|
||||
if tfHasMeta in i.value.flags:
|
||||
result = true
|
||||
break
|
||||
|
||||
proc collectVTableDispatchers*(g: ModuleGraph) =
|
||||
var itemTable = initTable[ItemId, seq[LazySym]]()
|
||||
var rootTypeSeq = newSeq[PType]()
|
||||
var rootItemIdCount = initCountTable[ItemId]()
|
||||
for bucket in 0..<g.methods.len:
|
||||
var relevantCols = initIntSet()
|
||||
if relevantCol(g.methods[bucket].methods, 1): incl(relevantCols, 1)
|
||||
sortBucket(g.methods[bucket].methods, relevantCols)
|
||||
let base = g.methods[bucket].methods[^1]
|
||||
let baseType = base.typ[1].skipTypes(skipPtrs-{tyTypeDesc})
|
||||
if baseType.itemId in g.objectTree and not containGenerics(baseType, g.objectTree[baseType.itemId]):
|
||||
let methodIndexLen = g.bucketTable[baseType.itemId]
|
||||
if baseType.itemId notin itemTable: # once is enough
|
||||
rootTypeSeq.add baseType
|
||||
itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen)
|
||||
|
||||
sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int =
|
||||
if x.depth >= y.depth: 1
|
||||
else: -1
|
||||
)
|
||||
|
||||
for item in g.objectTree[baseType.itemId]:
|
||||
if item.value.itemId notin itemTable:
|
||||
itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen)
|
||||
|
||||
var mIndex = 0 # here is the correpsonding index
|
||||
if baseType.itemId notin rootItemIdCount:
|
||||
rootItemIdCount[baseType.itemId] = 1
|
||||
else:
|
||||
mIndex = rootItemIdCount[baseType.itemId]
|
||||
rootItemIdCount.inc(baseType.itemId)
|
||||
for idx in 0..<g.methods[bucket].methods.len:
|
||||
let obj = g.methods[bucket].methods[idx].typ[1].skipTypes(skipPtrs)
|
||||
itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx])
|
||||
g.addDispatchers genVTableDispatcher(g, g.methods[bucket].methods, mIndex)
|
||||
else: # if the base object doesn't have this method
|
||||
g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, g.idgen)
|
||||
|
||||
proc sortVTableDispatchers*(g: ModuleGraph) =
|
||||
var itemTable = initTable[ItemId, seq[LazySym]]()
|
||||
var rootTypeSeq = newSeq[ItemId]()
|
||||
var rootItemIdCount = initCountTable[ItemId]()
|
||||
for bucket in 0..<g.methods.len:
|
||||
var relevantCols = initIntSet()
|
||||
if relevantCol(g.methods[bucket].methods, 1): incl(relevantCols, 1)
|
||||
sortBucket(g.methods[bucket].methods, relevantCols)
|
||||
let base = g.methods[bucket].methods[^1]
|
||||
let baseType = base.typ[1].skipTypes(skipPtrs-{tyTypeDesc})
|
||||
if baseType.itemId in g.objectTree and not containGenerics(baseType, g.objectTree[baseType.itemId]):
|
||||
let methodIndexLen = g.bucketTable[baseType.itemId]
|
||||
if baseType.itemId notin itemTable: # once is enough
|
||||
rootTypeSeq.add baseType.itemId
|
||||
itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen)
|
||||
|
||||
sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int =
|
||||
if x.depth >= y.depth: 1
|
||||
else: -1
|
||||
)
|
||||
|
||||
for item in g.objectTree[baseType.itemId]:
|
||||
if item.value.itemId notin itemTable:
|
||||
itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen)
|
||||
|
||||
var mIndex = 0 # here is the correpsonding index
|
||||
if baseType.itemId notin rootItemIdCount:
|
||||
rootItemIdCount[baseType.itemId] = 1
|
||||
else:
|
||||
mIndex = rootItemIdCount[baseType.itemId]
|
||||
rootItemIdCount.inc(baseType.itemId)
|
||||
for idx in 0..<g.methods[bucket].methods.len:
|
||||
let obj = g.methods[bucket].methods[idx].typ[1].skipTypes(skipPtrs)
|
||||
itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx])
|
||||
|
||||
for baseType in rootTypeSeq:
|
||||
g.setMethodsPerType(baseType, itemTable[baseType])
|
||||
for item in g.objectTree[baseType]:
|
||||
let typ = item.value.skipTypes(skipPtrs)
|
||||
let idx = typ.itemId
|
||||
for mIndex in 0..<itemTable[idx].len:
|
||||
if itemTable[idx][mIndex].sym == nil:
|
||||
let parentIndex = typ[0].skipTypes(skipPtrs).itemId
|
||||
itemTable[idx][mIndex] = itemTable[parentIndex][mIndex]
|
||||
g.setMethodsPerType(idx, itemTable[idx])
|
||||
@@ -21,4 +21,3 @@ when defined(nimStrictMode):
|
||||
# future work: XDeclaredButNotUsed
|
||||
|
||||
switch("define", "nimVersion:" & NimVersion)
|
||||
|
||||
|
||||
@@ -1606,6 +1606,8 @@ when not defined(js) and defined(nimV2):
|
||||
traceImpl: pointer
|
||||
typeInfoV1: pointer # for backwards compat, usually nil
|
||||
flags: int
|
||||
when defined(nimPreviewVtables) and not defined(cpp):
|
||||
vTable: UncheckedArray[pointer] # vtable for types
|
||||
PNimTypeV2 = ptr TNimTypeV2
|
||||
|
||||
proc supportsCopyMem(t: typedesc): bool {.magic: "TypeTrait".}
|
||||
|
||||
@@ -243,3 +243,8 @@ template tearDownForeignThreadGc* =
|
||||
|
||||
proc isObjDisplayCheck(source: PNimTypeV2, targetDepth: int16, token: uint32): bool {.compilerRtl, inl.} =
|
||||
result = targetDepth <= source.depth and source.display[targetDepth] == token
|
||||
|
||||
when defined(nimPreviewVtables) and not defined(cpp):
|
||||
proc nimGetVTable(p: pointer, index: int): pointer
|
||||
{.compilerRtl, inline, raises: [].} =
|
||||
result = cast[ptr PNimTypeV2](p).vTable[index]
|
||||
|
||||
@@ -44,4 +44,5 @@ switch("define", "nimPreviewNonVarDestructor")
|
||||
|
||||
switch("warningAserror", "UnnamedBreak")
|
||||
switch("legacy", "verboseTypeMismatch")
|
||||
switch("define", "nimPreviewVtables")
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
discard """
|
||||
matrix: "-u:nimPreviewVtables"
|
||||
output: '''(peel: 0, color: 15)
|
||||
(color: 15)
|
||||
17
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
multimethods:on
|
||||
24
tests/method/tcompilegenerics.nim
Normal file
24
tests/method/tcompilegenerics.nim
Normal file
@@ -0,0 +1,24 @@
|
||||
discard """
|
||||
matrix: "--mm:arc; --mm:refc"
|
||||
output: '''
|
||||
newDNode base
|
||||
'''
|
||||
"""
|
||||
|
||||
type
|
||||
SNodeAny = ref object of RootObj
|
||||
SNode[T] = ref object of SNodeAny
|
||||
m: T
|
||||
DNode[T] = ref object
|
||||
|
||||
method getStr(s: SNode[float]): string {.base.} = "blahblah"
|
||||
|
||||
method newDNode(s: SNodeAny) {.base.} =
|
||||
echo "newDNode base"
|
||||
|
||||
method newDNode[T](s: SNode[T]) =
|
||||
echo "newDNode generic"
|
||||
|
||||
let m = SNode[float]()
|
||||
let s = SNodeAny(m)
|
||||
newDnode(s)
|
||||
@@ -1,42 +1,42 @@
|
||||
discard """
|
||||
matrix: "--mm:arc; --mm:refc"
|
||||
output: '''wow2
|
||||
X 1
|
||||
X 3'''
|
||||
"""
|
||||
type
|
||||
First[T] = ref object of RootObj
|
||||
value: T
|
||||
|
||||
Second[T] = ref object of First[T]
|
||||
value2: T
|
||||
|
||||
method wow[T](y: int; x: First[T]) {.base.} =
|
||||
echo "wow1"
|
||||
|
||||
method wow[T](y: int; x: Second[T]) =
|
||||
echo "wow2"
|
||||
|
||||
var
|
||||
x: Second[int]
|
||||
new(x)
|
||||
|
||||
proc takeFirst(x: First[int]) =
|
||||
wow(2, x)
|
||||
|
||||
takeFirst(x)
|
||||
|
||||
|
||||
# bug #5479
|
||||
type
|
||||
Base[T: static[int]] = ref object of RootObj
|
||||
|
||||
method test[T](t: Base[T]) {.base.} =
|
||||
echo "X ", t.T
|
||||
|
||||
let ab = Base[1]()
|
||||
|
||||
ab.test()
|
||||
|
||||
let ac = Base[3]()
|
||||
ac.test()
|
||||
discard """
|
||||
matrix: "--mm:arc --multimethods:on -u:nimPreviewVtables; --mm:refc --multimethods:on -u:nimPreviewVtables"
|
||||
output: '''wow2
|
||||
X 1
|
||||
X 3'''
|
||||
"""
|
||||
type
|
||||
First[T] = ref object of RootObj
|
||||
value: T
|
||||
|
||||
Second[T] = ref object of First[T]
|
||||
value2: T
|
||||
|
||||
method wow[T](y: int; x: First[T]) {.base.} =
|
||||
echo "wow1"
|
||||
|
||||
method wow[T](y: int; x: Second[T]) =
|
||||
echo "wow2"
|
||||
|
||||
var
|
||||
x: Second[int]
|
||||
new(x)
|
||||
|
||||
proc takeFirst(x: First[int]) =
|
||||
wow(2, x)
|
||||
|
||||
takeFirst(x)
|
||||
|
||||
|
||||
# bug #5479
|
||||
type
|
||||
Base[T: static[int]] = ref object of RootObj
|
||||
|
||||
method test[T](t: Base[T]) {.base.} =
|
||||
echo "X ", t.T
|
||||
|
||||
let ab = Base[1]()
|
||||
|
||||
ab.test()
|
||||
|
||||
let ac = Base[3]()
|
||||
ac.test()
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
discard """
|
||||
matrix: "--mm:arc; --mm:refc"
|
||||
output: '''
|
||||
do nothing
|
||||
HELLO WORLD!
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
# tmethods1
|
||||
method somethin(obj: RootObj) {.base.} =
|
||||
echo "do nothing"
|
||||
|
||||
|
||||
type
|
||||
TNode* {.inheritable.} = object
|
||||
@@ -23,8 +20,6 @@ type
|
||||
method foo(a: PNode, b: PSomethingElse) {.base.} = discard
|
||||
method foo(a: PNodeFoo, b: PSomethingElse) = discard
|
||||
|
||||
var o: RootObj
|
||||
o.somethin()
|
||||
|
||||
|
||||
|
||||
|
||||
12
tests/method/tmethods_old.nim
Normal file
12
tests/method/tmethods_old.nim
Normal file
@@ -0,0 +1,12 @@
|
||||
discard """
|
||||
matrix: "--mm:arc -u:nimPreviewVtables"
|
||||
output: '''
|
||||
do nothing
|
||||
'''
|
||||
"""
|
||||
|
||||
# tmethods1
|
||||
method somethin(obj: RootObj) {.base.} =
|
||||
echo "do nothing"
|
||||
var o: RootObj
|
||||
o.somethin()
|
||||
@@ -1,96 +1,97 @@
|
||||
discard """
|
||||
output: '''
|
||||
collide: unit, thing
|
||||
collide: unit, thing
|
||||
collide: thing, unit
|
||||
collide: thing, thing
|
||||
collide: unit, thing |
|
||||
collide: unit, thing |
|
||||
collide: thing, unit |
|
||||
do nothing
|
||||
'''
|
||||
joinable: false
|
||||
disabled: true
|
||||
"""
|
||||
|
||||
|
||||
# tmultim2
|
||||
type
|
||||
TThing {.inheritable.} = object
|
||||
TUnit = object of TThing
|
||||
x: int
|
||||
TParticle = object of TThing
|
||||
a, b: int
|
||||
|
||||
method collide(a, b: TThing) {.base, inline.} =
|
||||
echo "collide: thing, thing"
|
||||
|
||||
method collide(a: TThing, b: TUnit) {.inline.} =
|
||||
echo "collide: thing, unit"
|
||||
|
||||
method collide(a: TUnit, b: TThing) {.inline.} =
|
||||
echo "collide: unit, thing"
|
||||
|
||||
proc test(a, b: TThing) {.inline.} =
|
||||
collide(a, b)
|
||||
|
||||
proc staticCollide(a, b: TThing) {.inline.} =
|
||||
procCall collide(a, b)
|
||||
|
||||
var
|
||||
a: TThing
|
||||
b, c: TUnit
|
||||
collide(b, c) # ambiguous (unit, thing) or (thing, unit)? -> prefer unit, thing!
|
||||
test(b, c)
|
||||
collide(a, b)
|
||||
staticCollide(a, b)
|
||||
|
||||
|
||||
|
||||
# tmultim6
|
||||
type
|
||||
Thing {.inheritable.} = object
|
||||
Unit[T] = object of Thing
|
||||
x: T
|
||||
Particle = object of Thing
|
||||
a, b: int
|
||||
|
||||
method collide(a, b: Thing) {.base, inline.} =
|
||||
quit "to override!"
|
||||
|
||||
method collide[T](a: Thing, b: Unit[T]) {.inline.} =
|
||||
echo "collide: thing, unit |"
|
||||
|
||||
method collide[T](a: Unit[T], b: Thing) {.inline.} =
|
||||
echo "collide: unit, thing |"
|
||||
|
||||
proc test(a, b: Thing) {.inline.} =
|
||||
collide(a, b)
|
||||
|
||||
var
|
||||
aaa: Thing
|
||||
bbb, ccc: Unit[string]
|
||||
collide(bbb, Thing(ccc))
|
||||
test(bbb, ccc)
|
||||
collide(aaa, bbb)
|
||||
|
||||
|
||||
|
||||
# tmethods1
|
||||
method somethin(obj: RootObj) {.base.} =
|
||||
echo "do nothing"
|
||||
|
||||
type
|
||||
TNode* {.inheritable.} = object
|
||||
PNode* = ref TNode
|
||||
|
||||
PNodeFoo* = ref object of TNode
|
||||
|
||||
TSomethingElse = object
|
||||
PSomethingElse = ref TSomethingElse
|
||||
|
||||
method foo(a: PNode, b: PSomethingElse) {.base.} = discard
|
||||
method foo(a: PNodeFoo, b: PSomethingElse) = discard
|
||||
|
||||
var o: RootObj
|
||||
o.somethin()
|
||||
discard """
|
||||
matrix: "--multimethods:on"
|
||||
output: '''
|
||||
collide: unit, thing
|
||||
collide: unit, thing
|
||||
collide: thing, unit
|
||||
collide: thing, thing
|
||||
collide: unit, thing |
|
||||
collide: unit, thing |
|
||||
collide: thing, unit |
|
||||
do nothing
|
||||
'''
|
||||
joinable: false
|
||||
disabled: true
|
||||
"""
|
||||
|
||||
|
||||
# tmultim2
|
||||
type
|
||||
TThing {.inheritable.} = object
|
||||
TUnit = object of TThing
|
||||
x: int
|
||||
TParticle = object of TThing
|
||||
a, b: int
|
||||
|
||||
method collide(a, b: TThing) {.base, inline.} =
|
||||
echo "collide: thing, thing"
|
||||
|
||||
method collide(a: TThing, b: TUnit) {.inline.} =
|
||||
echo "collide: thing, unit"
|
||||
|
||||
method collide(a: TUnit, b: TThing) {.inline.} =
|
||||
echo "collide: unit, thing"
|
||||
|
||||
proc test(a, b: TThing) {.inline.} =
|
||||
collide(a, b)
|
||||
|
||||
proc staticCollide(a, b: TThing) {.inline.} =
|
||||
procCall collide(a, b)
|
||||
|
||||
var
|
||||
a: TThing
|
||||
b, c: TUnit
|
||||
collide(b, c) # ambiguous (unit, thing) or (thing, unit)? -> prefer unit, thing!
|
||||
test(b, c)
|
||||
collide(a, b)
|
||||
staticCollide(a, b)
|
||||
|
||||
|
||||
|
||||
# tmultim6
|
||||
type
|
||||
Thing {.inheritable.} = object
|
||||
Unit[T] = object of Thing
|
||||
x: T
|
||||
Particle = object of Thing
|
||||
a, b: int
|
||||
|
||||
method collide(a, b: Thing) {.base, inline.} =
|
||||
quit "to override!"
|
||||
|
||||
method collide[T](a: Thing, b: Unit[T]) {.inline.} =
|
||||
echo "collide: thing, unit |"
|
||||
|
||||
method collide[T](a: Unit[T], b: Thing) {.inline.} =
|
||||
echo "collide: unit, thing |"
|
||||
|
||||
proc test(a, b: Thing) {.inline.} =
|
||||
collide(a, b)
|
||||
|
||||
var
|
||||
aaa: Thing
|
||||
bbb, ccc: Unit[string]
|
||||
collide(bbb, Thing(ccc))
|
||||
test(bbb, ccc)
|
||||
collide(aaa, bbb)
|
||||
|
||||
|
||||
|
||||
# tmethods1
|
||||
method somethin(obj: RootObj) {.base.} =
|
||||
echo "do nothing"
|
||||
|
||||
type
|
||||
TNode* {.inheritable.} = object
|
||||
PNode* = ref TNode
|
||||
|
||||
PNodeFoo* = ref object of TNode
|
||||
|
||||
TSomethingElse = object
|
||||
PSomethingElse = ref TSomethingElse
|
||||
|
||||
method foo(a: PNode, b: PSomethingElse) {.base.} = discard
|
||||
method foo(a: PNodeFoo, b: PSomethingElse) = discard
|
||||
|
||||
var o: RootObj
|
||||
o.somethin()
|
||||
|
||||
19
tests/method/tvtable.nim
Normal file
19
tests/method/tvtable.nim
Normal file
@@ -0,0 +1,19 @@
|
||||
type FooBase = ref object of RootObj
|
||||
dummy: int
|
||||
type Foo = ref object of FooBase
|
||||
value : float32
|
||||
type Foo2 = ref object of Foo
|
||||
change : float32
|
||||
method bar(x: FooBase, a: float32) {.base.} =
|
||||
discard
|
||||
method bar(x: Foo, a: float32) =
|
||||
x.value += a
|
||||
method bar(x: Foo2, a: float32) =
|
||||
x.value += a
|
||||
|
||||
|
||||
proc test() =
|
||||
var x = new Foo2
|
||||
x.bar(2.3)
|
||||
|
||||
test()
|
||||
Reference in New Issue
Block a user