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

sameType magic doesn't work for seq (and generics?) #14021

Open
mratsim opened this issue Apr 19, 2020 · 12 comments
Open

sameType magic doesn't work for seq (and generics?) #14021

mratsim opened this issue Apr 19, 2020 · 12 comments

Comments

@mratsim
Copy link
Collaborator

mratsim commented Apr 19, 2020

The sameType magic is supposed to allow type comparison between NimNode.
I expect that the use is similar to the is operator but in the VM.

It works for simple type like int but does not for seq[int], example

import macros

var i: int

macro testInt(i: typed): untyped =
  let res = i.sameType(bindSym"int")
  result = quote do:
    echo "i is int: ", bool(`res`), " (Nim VM `sameType`)"

testInt(i)

var x: seq[int]

macro testSeq(x: typed): untyped =
  let seqIntType = nnkBracketExpr.newTree(bindSym"seq", bindSym"int")
  let repr = repr seqIntType
  let res = x.sameType(seqIntType)
  result = quote do:
    echo "x is ", `repr`, ": ", bool(`res`), " (Nim VM `sameType`)"

testSeq(x)

macro foo(): untyped =
  result = nnkBracketExpr.newTree(bindSym"seq", bindSym"int")

var y: foo()
echo "y is seq[int]: ", y is seq[int], " (`is` operator)"
echo "x is same type as y: ", type(x) is type(y)

Output:

i is int: true (Nim VM `sameType`)
x is seq[int]: false (Nim VM `sameType`)
y is seq[int]: true (`is` operator)
x is same type as y: true

A very cumbersome workaround would be to delegate the comparison to "normal Nim" via when x is seq[int] and have that code call a delegate macro, if we have some context to pass to it we can pass it via a {.compileTime.} variable that stores NimNode.

This is another instance of "Working with types in macros is difficult" nim-lang/RFCs#44

@cooldome
Copy link
Member

cooldome commented Apr 19, 2020

IMO, it should be done this way.

macro testSeq2(x: typed): untyped =
  let seqIntType = getType(seq[int])[1]
  let repr = repr seqIntType
  let res = x.sameType(seqIntType)
  result = quote do:
    echo "x is ", `repr`, ": ", bool(`res`), " (Nim VM `sameType`)"
testSeq2(x)

bindSym does sym lookup only, it doesn't instantiate types.
Does such approach works for you?

mratsim added a commit to mratsim/Arraymancer that referenced this issue Apr 19, 2020
* index_select should use SomeInteger not SOmeNumber

* Overload index_select for arrays and sequences

* Masked Selector overload for openarrays

* Add masked overload for regular arrays and sequences

* Initial support of Numpy fancy indexing: index select

* Fix broadcast operators from #429 using deprecated syntax

* Stash dispatcher, working with types in macros is a minefield nim-lang/Nim#14021

* Masked indexing: closes #400, workaround nim-lang/Nim#14021

* Test for full masked fancy indexing

* Add index_fill

* Tensor mutation via fancy indexing

* Add tests for index mutation via fancy indexing

* Fancy indexing: supports broadcasting a value to a masked assignation

* Detect wrong mask or tensor axis length

* masked axis assign value test

* Add masked assign of broadcastable tensor

* Tag for changelog [skip ci]
@mratsim
Copy link
Collaborator Author

mratsim commented Apr 20, 2020

Unfortunately it does not, it works with seq but not with custom generic types:

type Tensor[T] = object
  data: T

macro testTensorInt(x: typed): untyped =
  let tensorIntType = getType(Tensor[int])[1]
  let repr = repr tensorIntType
  let res = x.sameType(tensorIntType)
  result = quote do:
    echo "x is ", `repr`, ": ", bool(`res`), " (Nim VM `sameType`)"

var x2: Tensor[int]
testTensorInt(x2)

@cooldome
Copy link
Member

Slightly improved version works

import macros 
type Tensor[T] = object
  data: T

macro testTensorInt(x: typed): untyped =
  let tensorIntType = getTypeInst(Tensor[int])[1]
  let repr = repr tensorIntType
  let res = x.sameType(tensorIntType)
  result = quote do:
    echo "x is ", `repr`, ": ", bool(`res`), " (Nim VM `sameType`)"

var x2: Tensor[int]
testTensorInt(x2)

@mratsim
Copy link
Collaborator Author

mratsim commented Apr 20, 2020

And this one doesn't work (the getType version didn't as well) when I want to access an abstract Tensor

type Tensor[T] = object
  data: T

macro testTensor(x: typed): untyped =
  let tensorType = getTypeInst(Tensor)
  let repr = repr tensorType
  let res = x.sameType(tensorType)
  result = quote do:
    echo "x is ", `repr`, ": ", bool(`res`), " (Nim VM `sameType`)"

var x2: Tensor[int]
testTensor(x2)

@mratsim
Copy link
Collaborator Author

mratsim commented Apr 20, 2020

I'm confused, the is operator works on NimNode as well? and typeof can convert from NimNode to typedesc, solving @bluenote10 issue #6785?

@cooldome
Copy link
Member

Typeof can extract type from anything that has type including NimNode if it is typed NimNode

@solo989
Copy link
Contributor

solo989 commented Apr 21, 2020

All you have done there is ask the question : NimNode is NimNode?
The answer will always be true. Not very useful.

@timotheecour
Copy link
Member

the bug is that sameType is error prone because it accepts nodes with no type
(ie, n: PNode with n.typ == nil)

instead, sameType API should be fixed to give CT error for nodes that don't have a type, like the one you built: let seqIntType = nnkBracketExpr.newTree(bindSym"seq", bindSym"int")

@cooldome
Copy link
Member

You can't build types like that: nnkBracketExpr.newTree(bindSym"seq", bindSym"int"). It will not work.

sameType proc checks if types are equal, but looks like what mratsim wants is isInstanceOf instead. macros module has isInstantiationOf proc but currently it works only for generic procs. IMO, best approach is to extend isInstantiationOf to cover object types as well.

@cooldome
Copy link
Member

If you want to compare to concrete types then sameType(x, getType(Tensor[int]) is your friend.
If you want to check if x is instance of Tensor type class. The sameType will not help.

@mratsim
Copy link
Collaborator Author

mratsim commented Apr 22, 2020

Ideally an equivalent to is for typed NimNode would solve everything, juggling with the getType, getTypeInst, getTypeImpl is already complex, it's best if we don't multiply the tools.

@timotheecour
Copy link
Member

timotheecour commented Apr 22, 2020

FWIW I don't like getType, getTypeInst, getTypeImp, typeKind, they're shoe-horning PType into a PNode constructed on the fly (look at vmdeps.mapTypeToAstX) and it creates artificial complexity for no benefit. The "implementation details" of PType already leaks through typeKind + getType anyways.

The compiler should simply embrace its strong typing and expose PNode as NimNode (as it does) and PType as NimType (as it doesn't do yet).

I have a branch that explores that.

Since PType objects can't be created/mutated by user (unlike PNode via macros), the API around NimType will be simpler (and simpler to implement in compiler!) than the one for NimNode, eg:

proc `[]`(a: NimType, index: int): NimType
proc kind(a: NimType): NimTypeKind # more type-safe than NimNode.typeKind
proc isType(a, b: NimType): bool # represents is
template sameType(a, b: NimType): bool = a.isType(b) and b.isType(a)
proc getType(a: NimNode): NimType

Benefits:

  • strongly-typed API, which is less error prone (unlike current one where everything is a PNode and lets you shoot yourself in the foot, exactly as @mratsim did in this issue)
  • more efficient since we don't need to create wrapping/unwrapping PType <=> PNode on the fly to implement getType (eg mapTypeToAstX + friends)
  • no breaking change, it just adds a new type (NimType) and new procs to deal with it
  • avoid multiplicating API's, the exposed ones would directly call the native PType apis

I believe this will help with your RFC [RFC] Working with types in macro is difficult. #44 nim-lang/RFCs#44

To your specific point regarding having is in macros, this would be proc isType(a, b: NimType): bool

D20200420T030114

mratsim added a commit to mratsim/constantine that referenced this issue Jan 21, 2021
mratsim added a commit to mratsim/constantine that referenced this issue Jan 21, 2021
* Introduce Fr type: finite field over curve order. Need workaround for nim-lang/Nim#16774

* Split curve properties into core and derived

* Attach field properties to an instantiated field instead of the curve enum

* Workaround nim-lang/Nim#14021, yet another "working with types in macros" is difficult nim-lang/RFCs#44

* Implement finite field over prime order of a curve subgroup

* skip OpenSSL tests on windows
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants