Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected behaviour on method dispatch #10038

Closed
zevv opened this issue Dec 18, 2018 · 2 comments

Comments

Projects
None yet
3 participants
@zevv
Copy link
Contributor

commented Dec 18, 2018

As reported on the Nim forum here: https://forum.nim-lang.org/t/4486#27999, I was asked to file this as an issue here:

type
  SNodeAny = ref object of RootObj
  SNode[T] = ref object of SNodeAny
  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 s = SNodeAny(SNode[float]())
newDnode(s)

What I expect to happen: variable s is a SNode[float] disguised as a SNodeAny. Calling newDnode(s) should dispatch the newDnode[T](s: SNode[T]) and the string newDNode generic should be printed.

Instead what happens is that the base method newDnode(s: SNodeAny) gets called, printing the string newDnode base

Now the strangest part: when the getStr() method is removed from the code, the behaviour changes to the expected behaviour, and all is well. Alternatively, removing the SNodeAny() cast from the second last line also restores the expected behaviour.

How does the existence of the getStr() method affect the behaviour of the newDNode() dispatch?

@zevv

This comment has been minimized.

Copy link
Contributor Author

commented Dec 18, 2018

For the record, @Araq's answer:

This works

type
  SNodeAny = ref object of RootObj
  SNode[T] = ref object of SNodeAny
  DNode[T] = ref object

method newDNode(s: SNodeAny) {.base.} =
  echo "newDNode base"

method newDNode[T](s: SNode[T]) =
  echo "newDNode generic"

method getStr(s: SNode[float]): string {.base.} = "blahblah"

let s = SNodeAny(SNode[float]())
newDnode(s)

why? The instantiation SNode[float] is cached and by that time there are no methods attached to SNode yet. Then the cache is consulted and the instantation of method newDNode[T](s: SNode[T]) is skipped. Multi methods and generics are broken beyond repair IMO.

Not sure how to process from here though, this feels a bit like a minefield - I guess I will play safe and try to redesign so that I don't need multimethods.

@mratsim

This comment has been minimized.

Copy link
Collaborator

commented Dec 19, 2018

This (or a mix with #10033) explains why I had this bug in Arraymancer mratsim/Arraymancer#327 with generics + methods + object variants "undeclared field" and had to remove all methods from my code base 3 days ago.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.