diff options
-rw-r--r-- | changelog.md | 1 | ||||
-rw-r--r-- | compiler/ccgtypes.nim | 20 | ||||
-rw-r--r-- | compiler/cgen.nim | 14 | ||||
-rw-r--r-- | compiler/cgmeth.nim | 23 | ||||
-rw-r--r-- | compiler/ic/cbackend.nim | 7 | ||||
-rw-r--r-- | compiler/ic/ic.nim | 12 | ||||
-rw-r--r-- | compiler/ic/integrity.nim | 4 | ||||
-rw-r--r-- | compiler/ic/replayer.nim | 15 | ||||
-rw-r--r-- | compiler/ic/rodfiles.nim | 4 | ||||
-rw-r--r-- | compiler/jsgen.nim | 5 | ||||
-rw-r--r-- | compiler/modulegraphs.nim | 40 | ||||
-rw-r--r-- | compiler/nim.cfg | 1 | ||||
-rw-r--r-- | compiler/pipelines.nim | 15 | ||||
-rw-r--r-- | compiler/sem.nim | 10 | ||||
-rw-r--r-- | compiler/sempass2.nim | 64 | ||||
-rw-r--r-- | compiler/vtables.nim | 167 | ||||
-rw-r--r-- | config/config.nims | 1 | ||||
-rw-r--r-- | lib/system.nim | 2 | ||||
-rw-r--r-- | lib/system/arc.nim | 5 | ||||
-rw-r--r-- | tests/config.nims | 1 | ||||
-rw-r--r-- | tests/generics/tobjecttyperel.nim | 1 | ||||
-rw-r--r-- | tests/method/nim.cfg | 1 | ||||
-rw-r--r-- | tests/method/tcompilegenerics.nim | 24 | ||||
-rw-r--r-- | tests/method/tgeneric_methods.nim | 84 | ||||
-rw-r--r-- | tests/method/tmethod_various.nim | 7 | ||||
-rw-r--r-- | tests/method/tmethods_old.nim | 12 | ||||
-rw-r--r-- | tests/method/tmultim.nim | 193 | ||||
-rw-r--r-- | tests/method/tvtable.nim | 19 |
28 files changed, 555 insertions, 197 deletions
diff --git a/changelog.md b/changelog.md index ab121f5a1..fb70a9a6b 100644 --- a/changelog.md +++ b/changelog.md @@ -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 diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 855ec4cd9..2f304852b 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -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) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 0011cb90e..5a331ae7c 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -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 diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 2cd5f3672..3bbe7b3f4 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -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) diff --git a/compiler/ic/cbackend.nim b/compiler/ic/cbackend.nim index c2e6b08fe..83f1b4cc7 100644 --- a/compiler/ic/cbackend.nim +++ b/compiler/ic/cbackend.nim @@ -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) = diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 867bc95ae..2f03ffb43 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -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 diff --git a/compiler/ic/integrity.nim b/compiler/ic/integrity.nim index 0ffd87c2f..d78e56847 100644 --- a/compiler/ic/integrity.nim +++ b/compiler/ic/integrity.nim @@ -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) = diff --git a/compiler/ic/replayer.nim b/compiler/ic/replayer.nim index 3bbe4fa31..b244ec885 100644 --- a/compiler/ic/replayer.nim +++ b/compiler/ic/replayer.nim @@ -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) diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index 968bf255f..5eef3874a 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -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. diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 74edb4694..7c636af8b 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -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) diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 99175815b..95fa193dc 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -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) diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 8488cb9ef..43da7e3e0 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -9,6 +9,7 @@ define:nimPreviewSlimSystem define:nimPreviewCstringConversion define:nimPreviewProcConversion define:nimPreviewRangeDefault +define:nimPreviewVtables define:nimPreviewNonVarDestructor threads:off diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim index b72207607..91f3428be 100644 --- a/compiler/pipelines.nim +++ b/compiler/pipelines.nim @@ -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): diff --git a/compiler/sem.nim b/compiler/sem.nim index 960fc9165..0dcc66432 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -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 diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 54f46fa73..e010bf179 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -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) diff --git a/compiler/vtables.nim b/compiler/vtables.nim new file mode 100644 index 000000000..f57b59eae --- /dev/null +++ b/compiler/vtables.nim @@ -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]) diff --git a/config/config.nims b/config/config.nims index 5c9c88e8a..4b8582d9c 100644 --- a/config/config.nims +++ b/config/config.nims @@ -21,4 +21,3 @@ when defined(nimStrictMode): # future work: XDeclaredButNotUsed switch("define", "nimVersion:" & NimVersion) - diff --git a/lib/system.nim b/lib/system.nim index 5d0bbb9c7..a072c5851 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -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".} diff --git a/lib/system/arc.nim b/lib/system/arc.nim index b49a6a63b..88d21643a 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -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] diff --git a/tests/config.nims b/tests/config.nims index e0de65fc4..0e58c7c14 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -44,4 +44,5 @@ switch("define", "nimPreviewNonVarDestructor") switch("warningAserror", "UnnamedBreak") switch("legacy", "verboseTypeMismatch") +switch("define", "nimPreviewVtables") diff --git a/tests/generics/tobjecttyperel.nim b/tests/generics/tobjecttyperel.nim index 80fe23459..0349184bb 100644 --- a/tests/generics/tobjecttyperel.nim +++ b/tests/generics/tobjecttyperel.nim @@ -1,4 +1,5 @@ discard """ + matrix: "-u:nimPreviewVtables" output: '''(peel: 0, color: 15) (color: 15) 17 diff --git a/tests/method/nim.cfg b/tests/method/nim.cfg deleted file mode 100644 index 57698b028..000000000 --- a/tests/method/nim.cfg +++ /dev/null @@ -1 +0,0 @@ -multimethods:on diff --git a/tests/method/tcompilegenerics.nim b/tests/method/tcompilegenerics.nim new file mode 100644 index 000000000..d0732895b --- /dev/null +++ b/tests/method/tcompilegenerics.nim @@ -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) \ No newline at end of file diff --git a/tests/method/tgeneric_methods.nim b/tests/method/tgeneric_methods.nim index ab92c66d8..0f1e7e1f0 100644 --- a/tests/method/tgeneric_methods.nim +++ b/tests/method/tgeneric_methods.nim @@ -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() diff --git a/tests/method/tmethod_various.nim b/tests/method/tmethod_various.nim index c41d04983..3b64aea8d 100644 --- a/tests/method/tmethod_various.nim +++ b/tests/method/tmethod_various.nim @@ -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() diff --git a/tests/method/tmethods_old.nim b/tests/method/tmethods_old.nim new file mode 100644 index 000000000..cd3f67217 --- /dev/null +++ b/tests/method/tmethods_old.nim @@ -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() diff --git a/tests/method/tmultim.nim b/tests/method/tmultim.nim index 126eb7526..bba4d8c5c 100644 --- a/tests/method/tmultim.nim +++ b/tests/method/tmultim.nim @@ -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() diff --git a/tests/method/tvtable.nim b/tests/method/tvtable.nim new file mode 100644 index 000000000..8d98dd42c --- /dev/null +++ b/tests/method/tvtable.nim @@ -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() \ No newline at end of file |