nim-lang / Nim

Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula. Its design focuses on efficiency, expressiveness, and elegance (in that order of priority).
https://nim-lang.org
Other
16.22k stars 1.46k forks source link

Can't create empty openArray with int8 #23752

Open arnetheduck opened 1 week ago

arnetheduck commented 1 week ago

Description

var v: array[1, byte]

echo v.toOpenArray(0'i8, -1'i8)

at runtime, this crashes with a Defect (!)

Nim Version

2.0.6

Current Output

Error: conversion from int8 to range 0..0(int) is invalid

Expected Output

No response

Possible Solution

No response

Additional Information

No response

litlighilit commented 1 week ago

Just

echo v.toOpenArray(0, -1)
litlighilit commented 1 week ago

The parameter type of first, last in toOpenArray[I, T](a: array[I, T], first, last: I) is I, not T.
e.g. in your code var v: array[1, byte], the generic type I is 0..0 (a slice of int), so the type of first, last shall be of 0..0 instead of int8.

Converting -1'i8 to 0..0 raises the error as shown above.

metagn commented 1 week ago

I wish the API had it so that the second argument was the length of the openarray, not the end index, that would probably make more sense for the construction of the openarray too since it stores the length. For the start/end index version we could have overloaded it for HSlice instead.

It's not really needed to fix this issue but we could still add an overload like toOpenArrayLen[I, T](a: array[I, T], start: I, len: int), so we could instead do:

v.toOpenArrayLen(0'i8, 0)
arnetheduck commented 1 week ago

the sanity ship of slice indexing in nim sailed, so we must do with what we have unfortunately.

an empty array is toOpenArray(0, -1) because the second argument is inclusive and must therefore be -1 to point to the same location as the 0 - in particular, 0..0 is a slice of length 1.

int8 should promote to int in all relevant aspects - this is a conversion bug.

it's a lot more dangerous at runtime:

var v: int
var v8: int8
var a: array[1, byte]

echo a.toOpenArray(v, v-1)
echo a.toOpenArray(v8, v8-1) # crashes here - obviously you should be able to use int8 like this.
ringabout commented 3 days ago

@arnetheduck toOpenArray works for this case because proc toOpenArray*[T](x: openArray[T]; first, last: int): openArray[T] takes precedence when both ends are integer.

var v: int
var a: array[1, byte]

echo a.toOpenArray(v, v-1)

So when implementing a toOpenarray function

proc toOpenArray2*[I, T](x: array[I, T]; first, last: I) =
  echo x

proc foo =
  var v: array[1, byte]
  toOpenArray2(v, 0, -1)

foo()

It gives Error: conversion from int literal(-1) to range 0..0(int) is invalid as expected

arnetheduck commented 3 days ago

It gives Error: conversion from int literal(-1) to range 0..0(int) is invalid as expected

I'm not sure I understand your comment.

toOpenArray(0, -1) is a valid slice over array[1, byte] - in particular, it's the empty openarray.

toOpenArray(0'i8, -1'i8) is also a valid slice range over array[1, byte] - the same one. Both should work without compile-time error and/or crashes at runtime.

See https://play.nim-lang.org/#pasty=dAQERzuy

[]
/usercode/in.nim(6)      in
/playground/nim/lib/system/fatal.nim(53) sysFatal
Error: unhandled exception: value out of range: -1 notin 0 .. 0 [RangeDefect]

we can see the empty openArray being printed, then the crash happening - it should have the same behavior regardless of int type.

ringabout commented 3 days ago

I explained why this happens. I'm not saying the API is reasonable.

proc toOpenArray*[I, T](x: array[I, T]; first, last: I): openArray[T] {.
  magic: "Slice".}

Isn't array[1, byte] actually array[0..0, byte]? So the type of first and last is range[0..0]. It cannot accept -1 which is out of range. so toOpenArray(0'i8, -1'i8) doesn't work since it can only match this one.

It is consistent with var x: range[0..0] = -1.

toOpenArray(0, -1) works because it matches proc toOpenArray*[T](x: openArray[T]; first, last: int): openArray[T] instead of proc toOpenArray*[I, T](x: array[I, T]; first, last: I): openArray[T].

Araq commented 3 days ago

I think there is no type resolution bug here but the code generation for some variant of magic: "Slice" is overly picky.