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

Option causes a crash on distinct ref types #9566

Open
PMunch opened this issue Oct 30, 2018 · 3 comments
Open

Option causes a crash on distinct ref types #9566

PMunch opened this issue Oct 30, 2018 · 3 comments

Comments

@PMunch
Copy link
Contributor

PMunch commented Oct 30, 2018

Noticed a very strange bug with options and distinct types (not that I think the source of the issue is related to either). The supplied example throws an error unless -d:passTest is passed when compiling. The error comes from the C compiler which complains that a struct doesn't have the field has. The options module tries to optimise options of ref | ptr | pointer to just be a nil check instead of having a separate has field. Nim appears to not see the distinct JsonNode as a ref type, but it is able to see that Option[TestType] should be the same as a Option[JsonNode]. This means that if somelongname (named to be easy to search for in the C source) is defined before anotherlongname then the resulting Option struct doesn't have a has field as JsonNode is a ref type. But if the order is different, or if somelongname is not defined at all, then the resulting Option struct has the field as it doesn't see TestType as a ref.

Example

import json
import options

type TestType = distinct JsonNode

when not defined(passTest):
  var somelongname: Option[JsonNode]

var anotherlongname: Option[TestType]

if anotherlongname.isNone:
  echo "as expected"

Current Output

Without -d:passTest

Error: execution of an external compiler program 'gcc -c  -w  -I/home/peter/.choosenim/toolchains/nim-0.19.0/lib -o /home/peter/.cache/nim/jsopttest_d/jsopttest.c.o /home/peter/.cache/nim/jsopttest_d/jsopttest.c' failed with exit code: 1

/home/peter/.cache/nim/jsopttest_d/jsopttest.c: In function ‘isNone_bL5Zl5L69br9bckzAxrvAwMwjsopttest’:
/home/peter/.cache/nim/jsopttest_d/jsopttest.c:157:17: error: ‘tyObject_Option_Cgz4GLZ9bQIhJokNqwA3eBQ’ {aka ‘struct tyObject_Option_Cgz4GLZ9bQIhJokNqwA3eBQ’} has no member named ‘has’
  result = !(self.has);
                 ^

With -d:passTest

Hint: operation successful (26061 lines compiled; 1.025 sec total; 38.906MiB peakmem; Debug Build) [SuccessX]

Additional Information

I ran into this when trying to use my jsonschema library to create nimlsp, and managed to condense the error down to this.

EDIT: Found smaller example and probable root cause (updated example above).
EDIT2:
After some more testing I found out that this is the issue:

type
  MyRefType = ref object
    discard
  TestType = distinct MyRefType

when TestType is ref:
  echo "TestType is ref"

when MyRefType is ref:
  echo "MyRefType is ref"

This will output "MyRefType is ref", but not "TestType is ref". But Nim is still able to see that these are the same type, meaning that things like the option will be reused, but the two types will behave differently.

@PMunch
Copy link
Contributor Author

PMunch commented Oct 30, 2018

A workaround to my initial issue is to do something like this:

import json
import options

type
  TestTypeObj = distinct JsonNodeObj
  TestType = ref TestTypeObj

var somelongname: Option[JsonNode]
var anotherlongname: Option[TestType]

if anotherlongname.isNone:
  echo "as expected"

This will allow TestType to be seen as a ref, and it's still compatible with a JsonNode as it is defined as a ref JsonNodeObj.

@krux02
Copy link
Contributor

krux02 commented Oct 9, 2019

I gave this issue a shot today, but I was not succesful. This is what I tried:

import typetraits, macros

macro isSomePointer(t: typedesc): bool =
  var impl = t.getTypeInst[1].getTypeImpl
  if impl.kind == nnkDistinctTy:
    impl = impl[0].getTypeImpl
  if impl.kind in {nnkPtrTy,nnkRefTy}:
    result = newLit(true)
  elif impl.kind == nnkSym and impl.eqIdent("pointer"):
    result = newLit(true)
  else:
    result = newLit(false)

type
  Option*[T] = object
    ## An optional type that stores its value and state separately in a boolean.
    when isSomePointer(T):
      val: T
    else:
      val: T
      has: bool

  UnpackError* = object of Defect

The problem is, the macro is not resolved, when the generic is instantiated, it is resolved immediately and the getTypeImpl call won't return anything useful here.

@krux02 krux02 changed the title C compiler error when using options and distinct types Option causes a crash on distinct ref types Oct 9, 2019
@Araq
Copy link
Member

Araq commented Oct 16, 2019

This still produces a codegen error.

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

3 participants