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

Template called when arguments signature doesn't match #21774

Closed
al6x opened this issue May 3, 2023 · 6 comments
Closed

Template called when arguments signature doesn't match #21774

al6x opened this issue May 3, 2023 · 6 comments

Comments

@al6x
Copy link

al6x commented May 3, 2023

Description

The build_html template called. It shouldn't be called, as the call signature doesn't match - there's no tname: string arg supplied.

type El* = ref object
  tag*:           string
  children*:      seq[El]

# template build_html(blk): seq[El] =
#   block:
#     let it {.inject.} = El(tag: "div")
#     blk
#     it.children

template build_html(tname: string, blk): seq[El] =
  block:
    let it {.inject.} = El(tag: tname)
    blk
    it.children

template h*(tname: string) =
  it.children.add El(tag: tname)

discard build_html: # <= no `tname: string` arg, why `build_html` called?
  h"div"

It ends up with the error, but with the wrong error.

play.nim(24, 4) template/generic instantiation of `h` from here
play.nim(14, 3) Error: undeclared identifier: 'it'

Another problem - what I actually wanted to do is to have two versions of build_html and it doesn't work. If you uncomment the overloaded version, it won't be called, the wrong build_html will be called.

Nim Version

nim -v
Nim Compiler Version 1.6.12 [MacOSX: amd64]
Compiled at 2023-03-10
Copyright (c) 2006-2023 by Andreas Rumpf

active boot switches: -d:release -d:nimUseLinenoise

Current Output

No response

Expected Output

No response

Possible Solution

No response

Additional Information

No response

@metagn
Copy link
Collaborator

metagn commented May 3, 2023

nim-lang/RFCs#402

Workaround is

template buildHtmlImpl(tname: string, blk): seq[El] =
  block:
    let it {.inject.} = El(tag: tname)
    blk
    it.children

template build_html(blk): seq[El] =
  buildHtmlImpl("div", blk)

template build_html(tname: untyped, blk): seq[El] =
  buildHtmlImpl(tname, blk)

@al6x
Copy link
Author

al6x commented May 4, 2023

@metagn It's not looking like RFC, it's looking like a bug that should be fixed.

Different bug, but seems to be caused by same problem

type El* = ref object
  tag*:           string
  children*:      seq[El]

template build_h*(html: string, blk): El =
  block:
    let it {.inject.} = El(tag: html)
    blk
    it

template h*(html: string) =
  it.children.add El(tag: html)

template h*(html: string, code) =
  let parent = it
  block:
    let it {.inject.} = El(tag: html)
    code
    parent.children.add it

template section*(content) =
  h"section":
    content

# Comment out this and result will be different
template section*(title: string, content) =
  h"section":
    content

proc `$`*[T: typed](x: ref T): string = "->" & $(x[])

echo:
  build_h"app":
    section:
      h"button"

Result will be app(section, button) while it should be app(section(button)) if you comment out the unused template, the output would be correct.

@metagn
Copy link
Collaborator

metagn commented May 4, 2023

I just linked the RFC because it's the closest thing to a collection of these issues, sorry if that wasn't clear. If you want a list of issues with the same problem, here you go #14827 #19556 #20274, Your example there breaks because of the same issue causing the upper it created by build_h to get bound inside h"button" instead of the one created by section. The generalized workaround I wrote in one of the other issues is like:

template sectionImpl(content: untyped, title: string) =
  h"section": content

template sectionImpl(content: untyped) =
  h"section": content

template section(title, content: untyped) =
  sectionImpl(content, title)

template section(content: untyped) =
  sectionImpl(content)

@Araq
Copy link
Member

Araq commented May 4, 2023

There is no bug here, it's just that I misread your code on the forum.

The compiler check's h"div" to see which if any of build_html to call and this check fails as the expansion of h"div" uses an undeclared it.

It's actually quite easy to understand when you don't treat the Nim compiler as an almighty AI overload.

@Araq Araq closed this as completed May 4, 2023
@al6x
Copy link
Author

al6x commented May 4, 2023

@Araq but templates can have undeclared variables. The code below with undeclared it would work, but as soon as you overload build_html (the commented out code) it fails.

That's just looks unexpected - you have working code, then add one more template (that don't even match the call signature and shouldn't even be called), and suddenly code that previously worked fail. (I see that possible solution is to use tname: untyped instead of tname: string)

type El* = ref object
  tag*:           string
  children*:      seq[El]

template build_html(blk): seq[El] =
  block:
    let it {.inject.} = El(tag: "div")
    blk
    it.children

# template build_html(tname: string, blk): seq[El] =
#   block:
#     let it {.inject.} = El(tag: tname)
#     blk
#     it.children

template h*(tname: string) =
  it.children.add El(tag: tname)

discard build_html: # <= no `tname: string` arg, why `build_html` called?
  h"div"

@Araq
Copy link
Member

Araq commented May 5, 2023

That's just untyped's misfeatures in action, but your build_html is not called.

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

No branches or pull requests

3 participants