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

fixes #23755; array static inference during overload resolution #23760

Merged
merged 14 commits into from
Jul 1, 2024

Conversation

Graveflo
Copy link
Contributor

@Graveflo Graveflo commented Jun 26, 2024

@Graveflo Graveflo marked this pull request as draft June 26, 2024 04:06
@mratsim
Copy link
Collaborator

mratsim commented Jun 26, 2024

We should add both test cases you mentioned in #23755 (comment). If it's intentional because you will address the first one later, carry on.

@Graveflo
Copy link
Contributor Author

We should add both test cases you mentioned in #23755 (comment). If it's intentional because you will address the first one later, carry on.

Yea I don't have a lot of time to work on this. Im just just leaving it here drafted as I make progress. Plus, the CI gets effed up every time I try to run it locally and my my repos so I use that here sometimes.

@Graveflo
Copy link
Contributor Author

CI failing, but idk, it looks like http connection issues. This is where I'm at anyway:

Both of these "fixes" are dirty workarounds. The code near getOrdValue might actually be a decent idea, but the code added in inferStaticsInRange is not. I still think this incompatibility needs to be flushed out earlier. It's far less then ideal to decide if input is valid by bouncing them off of those ord procs.

I can't help but feel like adding more asserts to specific procs would guard against them being abused and expose logic that is not quite right.

Also, the check for the longer standing bug is just a patch. It's probably fragile.

@Graveflo Graveflo marked this pull request as ready for review June 29, 2024 17:03
@Graveflo Graveflo changed the title trying to fix 23755 fixes #23755; array static inference during overload resolution Jun 29, 2024
@Graveflo
Copy link
Contributor Author

Although this is kinda obvious, I should mention what is happening:

In typeRel's tyArray branch the range of the array is processed via inferStaticsInRange. In that proc, families of functions like lengthOrd, lastOrd, and eventually getOrdValue are called that work with Int128 types. There was a comment in getOrdValue warning that the current error handling scheme (returning high(Int128)) was probably a bad idea due to overflow concerns. inferStaticsInRange takes the Int128 and converts it with toInt64, which from an erroneous call to getOrdValue is not compatible, triggering the assertion error from the issue.

As I said before, I think there is something else wrong with this besides avoiding the overflow. Maybe the node structure is not what is expected.

compiler/types.nim Outdated Show resolved Hide resolved
@@ -1279,7 +1282,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
result = isGeneric
else:
result = typeRel(c, ff, aa, flags)


if fRange.kind == tyArray and fRange.indexType.kind == tyGenericParam:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fRange.kind == tyArray doesn't make sense, I think the lines above are wrong:

if prev == nil:
  put(c, fRange, a.indexType)
  fRange = a

fRange = a should be fRange = a.indexType, then we can just check if fRange.kind == tyGenericParam.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm of the opinion that the whole tyArray branch of typeRel needs to be revisited, not just this part. I didn't try to mess with what was already there. Sorry this PR is so patchy. It would take me a while to understand how the statics are packed into the node structure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And to be clear, I did look at that briefly and fRange = a makes zero sense to me at all, so if you have an idea about that you are further along then me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait, something just clicked for me. fRange is being set to a because if it has no binding it's assumed that it can accept a, so that is the substitution. What if the generic parameter has constraints though? I think that might be another error. Just because the range is a generic type variable doesn't mean it accepts any type. That's an unconstrained generic type variable.

@Graveflo
Copy link
Contributor Author

If this passes the CI, it seems like the workaround can be removed from inferStaticsInRange as well (at least for the givin test cases). I'll probably be asleep by the time it finishes though

var err = false
let lenOrd = getOrdValueAux(concrete.n[1], err)
if err:
return isNone
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think an alternative to this would be to change the code below:

if fRange.rangeHasUnresolvedStatic:
  return inferStaticsInRange(c, fRange, a)

to:

if fRange.rangeHasUnresolvedStatic:
  if not aRange.rangeHasUnresolvedStatic:
    return inferStaticsInRange(c, fRange, a)
  else:
    return isNone

The issue with the ord value failing is that f and a both have unresolved statics (which is supposed to happen, checkGeneric in cmpCandidates compares generic overloads to each other), and the inferStaticsInRange tries to evaluate unresolved statics in a to assign to the unresolved statics in f and crashes because the unresolved statics don't have values. In this case calling inferStaticsInRange at all doesn't make sense, we can deal with it early on by checking if a has unresolved statics.

This change compiles the test but idk if it'll break anything in CI in general.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per my comment above the test cases that bring us to this point are avoided by the extra call to typeRel from the "try this" push. If that is a good sanity check then maybe it's worth trying it out anyway. Maybe run the CI with the

proc view(a: Limbs) = discard
proc view(a: var Limbs) = discard

overload scenario on top of devel to see if it's worth merging before this PR?

compiler/sigmatch.nim Outdated Show resolved Hide resolved
@narimiran
Copy link
Member

Even after the fixes from this PR, there is still an Error: fatal error: invalid kind for lastOrd(tyGenericParam) in an example that @tersec and I are trying to minimize and make self-contained.
I've bisected the problem, and the root cause seems to be the same: #23137

Don't know if this stops the merge of this PR, or if it will be fixed in some future PR.

@Araq
Copy link
Member

Araq commented Jul 1, 2024

I'm merging it already as the change is a definite improvement.

@Araq Araq merged commit 27abcdd into nim-lang:devel Jul 1, 2024
18 checks passed
@narimiran
Copy link
Member

narimiran commented Jul 1, 2024

Here is a self-contained example that worked before #23137 and it fails with Error: fatal error: invalid kind for lastOrd(tyGenericParam) after it (including this PR):

func `xor`[T: array](a, b: T): T =
  for i in 0..<result.len:
    result[i] = a[i] xor b[i]

template eachElement(x, y, res, op: untyped) =
  for i in 0..<res.len:
    res[i] = op(x[i], y[i])

func `xor`*[N: static int; T](x, y: array[N, T]): array[N, T] =
  eachElement(x, y, result, `xor`)

var a: array[5, int]
var b: array[5, int]

discard a xor b

Copy link
Contributor

github-actions bot commented Jul 1, 2024

Thanks for your hard work on this PR!
The lines below are statistics of the Nim compiler built from 27abcdd

Hint: mm: orc; opt: speed; options: -d:release
179051 lines; 8.471s; 664.711MiB peakmem

@narimiran
Copy link
Member

narimiran commented Jul 10, 2024

Here is a self-contained example that worked before #23137 and it fails with Error: fatal error: invalid kind for lastOrd(tyGenericParam) after it (including this PR):

func `xor`[T: array](a, b: T): T =
  for i in 0..<result.len:
    result[i] = a[i] xor b[i]

template eachElement(x, y, res, op: untyped) =
  for i in 0..<res.len:
    res[i] = op(x[i], y[i])

func `xor`*[N: static int; T](x, y: array[N, T]): array[N, T] =
  eachElement(x, y, result, `xor`)

var a: array[5, int]
var b: array[5, int]

discard a xor b

Is anyone looking into fixing the error from the above example? Should I open a separate issue for it?

EDIT: Ok, I created a new issue for it: #23823

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

Successfully merging this pull request may close these issues.

None yet

5 participants