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:
ringabout
2023-11-28 22:11:43 +08:00
committed by GitHub
parent 8cad6ac048
commit 30cf33f04d
28 changed files with 555 additions and 197 deletions

View File

@@ -1 +0,0 @@
multimethods:on

View 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)

View File

@@ -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()

View File

@@ -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()

View 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()

View File

@@ -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
View 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()