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

overloadExists(foo(args)) to tell whether an overload exists without compiling/running body via compiles #164

Open
timotheecour opened this issue Aug 25, 2019 · 4 comments

Comments

@timotheecour
Copy link
Member

timotheecour commented Aug 25, 2019

(originally discussed here: nim-lang/Nim#11722 (comment) /cc @krux02 )

proposal1

proc overloadExists(expr: untyped): bool {.magic:"overloadExists".} # in system.nim (needed for `untyped` in proc)
  • overloadExists(foo(args)) compiler magic that checks whether foo(args) has a proper overload; it doesn't try to compile foo(args) ; in particular, the check succeeds even if foo(args) has a fwd declaration and no definition.

examples:

doAssert overloadExists(toUpper("abc"))
doAssert not overloadExists(toUpper(12))
doAssert overloadExists(1+1)
doAssert not overloadExists(1+1.2)
doAssert not overloadExists(nonexistant(1))

proposal2

independent / orthogonal to proposal1. This selects a particular overload via overloadResolve. Works with any routine (template/macro/iterator/generics, regardless of all optional params/varargs)

const fooParticularOverload = overloadResolve foo("1", true)
echo fooParticularOverload("bar", false) 

# this is helpful for debugging among other use cases:
echo inspect(fooParticularOverload) # can be done via https://github.com/nim-lang/Nim/pull/11754 or even just #11992
# would print:
template fooParticularOverload(x: string|cstring, y = false, z = 0): untyped declared at foo/bar.nim(12,14)

this 2nd proposal requires nim-lang/Nim#11992

rationale

depending on compiles(foo(args)) can be problematic for a number of reasons:

  • compiles causes CT overhead, which can be significant depending on what's being compiled; eg:
when compiles(expensiveCalculation(args)): # foo(args) executed here
  expensiveCalculation(args) # executed here again
else: workaround(args)
  • compiles can have side effects, eg:
proc foo() =
  const s = gorge("echo bar > /tmp/z01.txt")
  echo s
when compiles(foo()): echo "ok1"
else: echo "ok2"

The side effect can even be executed when the compiles() eventually fails after the side effect

  • the compiles(foo(args)) can fail for a reason the user did not expect, eg a bug in proc body; that bug may end up not getting caught; eg:
proc fun[T: Ordinal | Foo1 | Foo2](a: T) = # contains a bug that affects some `T`
proc workaround[T](a: T) = discard
when compiles(fun(args)): # fails for some T in `Ordinal | Foo1 | Foo2` 
  fun(args)
else: workaround(args)

here user expects fun(args) to compile for every T in Ordinal | Foo1 | Foo2 but in fact some cases may trigger the other branch to be taken due to a bug inside fun

@mratsim
Copy link
Collaborator

mratsim commented Aug 25, 2019

Does it need to be magic? Or can we lift something already implemented in sigmatch?
I think it should be in typetraits, typetraits is used in system.nim for deepCopy/supportsCopyMem.

If it helps I wrote my own overload resolution macro for my needs though it requires a typed input

@timotheecour
Copy link
Member Author

timotheecour commented Aug 26, 2019

Does it need to be magic?

I don't see how that could work without relying on a {.magic.}?

Or can we lift something already implemented in sigmatch?

yes, it would leverage sigmatch, the basic functionality to implement this is already there ; unless I'm missing something, a PR implementing proposal1 shouldn't be hard

If it helps I wrote my own overload resolution macro for my needs though it requires a typed input

well typed input would be "too late", since we don't know whether overload exists

I think it should be in typetraits, typetraits is used in system.nim

yes, that's the other sensible module. it would logically group well with system.declared, system.compiles, but I understand we want to avoid growing system.

@Araq
Copy link
Member

Araq commented Aug 27, 2019

system.compiles is incredibly underspecified and needs a spec and needs to disallow some of its wild west usages. After we have done that, I'm not sure we need more builtins with overlapping functionality.

@metagn
Copy link
Contributor

metagn commented Nov 7, 2022

Funny hack

proc plusExists[T]: bool =
  type Foo = concept
    proc `+`(x, y: T): T
  result = void is Foo
echo plusExists[int]() 
echo plusExists[string]()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants