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

handle explicit generic routine instantiations in sigmatch #24010

Merged
merged 23 commits into from
Sep 2, 2024

Conversation

metagn
Copy link
Collaborator

@metagn metagn commented Aug 24, 2024

fixes #16376

The way the compiler handled generic proc instantiations in calls (like foo[int](...)) up to this point was to instantiate foo[int], create a symbol for the instantiated proc (or a symchoice for multiple procs excluding ones with mismatching generic param counts), then perform overload resolution on this symbol/symchoice. The exception to this was when the called symbol was already a symchoice node, in which case it wasn't instantiated and overloading was called directly (these lines).

This has several problems:

The way overload resolution handles explicit instantiations by itself is also buggy:

  • It doesn't check constraints.
  • It allows only partially providing the generic parameters, which makes sense for implicit generics, but can cause ambiguity in overloading.

Here is how this PR deals with these problems:

  • Overload resolution now always handles explicit generic instantiations in calls, in initCandidate, as long as the symbol resolves to a routine symbol.
  • Overload resolution now checks the generic params for constraints and correct parameter count (ignoring implicit params). If these don't match, the entire overload is considered as not matching and not instantiated.
  • Special error messages are added for mismatching/missing/extra generic params. This is almost all of the diff in semcall.
  • Procs with matching generic parameters now instantiate only the type of the signature in overload resolution, not the proc itself, which also works for templates and macros.

Unfortunately we can't entirely remove instantiations because overload resolution can't handle some cases with uninstantiated types even though it's resolved in the binding (see the last 2 blocks in texplicitgenerics). There are also some instantiation issues with default params that #24005 didn't fix but I didn't want this to become the 3rd huge generics PR in a row so I didn't dive too deep into trying to fix them. There is still a minor instantiation fix in semtypinst though for subscripts in calls.

Additional changes:

  • Overloading of [] wasn't documented properly, it somewhat is now because we need to mention the limitation that it can't be done for generic procs/types.
  • Tests can now enable the new type mismatch errors with just -d:testsConciseTypeMismatch in the command.

Package PRs:

  • using fork for now: combparser (partial generic instantiation)
  • merged: cligen (partial generic instantiation but non-overloaded + template)
  • merged: neo (trying to instantiate template with no generic param)

@metagn
Copy link
Collaborator Author

metagn commented Aug 25, 2024

While developing this PR initially I didn't know what to do with partial generic instantiations, i.e. instantiations with missing generic parameters, before I decided to just disallow them and PR packages which had them. This was the comment I wrote about it:

There is a problem remaining of what to do with partial generic instantiations. This is in tests/generics/timplicit_and_explicit:

proc doStuff[T;Y](a: SomeInteger, b: Y): Y = discard
assert typeof(doStuff[int](100, 1.0)) is float

And a package uses it as evidenced by this error:

protobuf/src/protobuf/private/parse.nim(154, 158) template/generic instantiation of `flatMap` from here
combparser/combparser.nim(365, 37) Error: type mismatch
Expression: Nothing[(U, string)](xresult, "Unable to flat-map onto bad output", input)
  [1] xresult: Maybe[(string, string), system.string]
  [2] "Unable to flat-map onto bad output": string
  [3] input: string
  
Expected one of (first mismatch at [position]):
[1] proc Nothing[T, U, V, W](left: Maybe[U, W]; right: Maybe[V, W]; error: string;
                         input: W): Maybe[T, W]
  missing generic parameter: U
[1] proc Nothing[T, U, V](old: Maybe[U, V]): Maybe[T, V]
  missing generic parameter: U
[1] proc Nothing[T, U, V](old: Maybe[U, V]; error: string; input: V): Maybe[T, V]
  missing generic parameter: U
[1] proc Nothing[T, U](error: string; input: U): Maybe[T, U]
  missing generic parameter: U

It's supposed to emulate doing something like Nothing[(U, string), auto, auto, ...] where the number of autos is the number of remaining generic procs of each overload (you can't actually use auto like this but we can implement it), but it can crash the compiler in cases like:

proc foo[I, T](a: array[I, T]) = discard
proc foo[T](x: openarray[T]) = discard
foo[string]([1, 2, 3]) # invalid type kind for lastOrd: string

Specifically a test which does `@`[string] crashes, so we could "work around" this by not allowing partial instantiation of mArrToSeq, but you can encounter it in user code as shown above. We could also constrain I to get around it but no one will want to write array types that way and there's no good choice of constraint for all array index types. We could try to fix array types too, so that they don't match if their index type isn't an ordinal, but I think this is a general problem that can cause all sorts of issues.

The first thing I tried was only allowing non-overloaded procs to be partially instantiated, but the above cases failed. So my question is do we:

  1. keep this behavior and remove the above cases (PR the package & change the test)
  2. remove it for all procs regardless of being overloaded and potentially break even more code
  3. allow partial instantiation somehow

The 3rd one seems the most impossible to me, the 2nd one gives us more work of PRing packages, the 1st one shouldn't cause issues but is still weird design, the instant you add an overload it gives an error. On top of 1 and 2 we can also add the auto thing.

Edit: PR'd the package anyway: PMunch/combparser#6, also the original PR that added the test just seems like it made a mistake

Edit: Also cligen, seems like a non-overloaded case:

cligen/argcvt.nim(171, 27) Error: type mismatch
  Expression: doArgParse[BiggestInt](parseBiggestInt, dst, dfl, a)
    [1] parseBiggestInt: proc (s: string): BiggestInt{.noSideEffect, gcsafe.} | proc (s: openArray[char], number: var BiggestInt): int{.raises: [ValueError], noSideEffect, gcsafe.} | proc (s: string, number: var BiggestInt, start: int): int{.raises: [ValueError], noSideEffect, gcsafe.}
    [2] dst: var int
    [3] dfl: int
    [4] a: var ArgcvtParams
  
  Expected one of (first mismatch at [position]):
  [1] template doArgParse[WideT: SomeNumber; T: SomeNumber](parse: untyped;
      dst: var T; dfl: T; a: var ArgcvtParams): bool
    missing generic parameter: T

One takeaway is that we can probably still support inferring generic parameters without instantiating them by writing auto in place of them, this has a trivial implementation.

Araq pushed a commit that referenced this pull request Aug 30, 2024
closes #1969, closes #7547, closes #7737, closes #11838, closes #12283,
closes #12714, closes #12720, closes #14053, closes #16118, closes
#19670, closes #22645

I was going to wait on these but regression tests even for recent PRs
are turning out to be important in wide reaching PRs like #24010.

The other issues with the working label felt either finnicky (#7385,
#9156, #12732, #15247), excessive to test (#12405, #12424, #17527), or I
just don't know what fixed them/what the issue was (#16128: the PR link
gives a server error by Github, #12457, #12487).
@metagn metagn marked this pull request as ready for review August 31, 2024 22:25
@Araq Araq merged commit 71de7fc into nim-lang:devel Sep 2, 2024
19 checks passed
Copy link
Contributor

github-actions bot commented Sep 2, 2024

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

Hint: mm: orc; opt: speed; options: -d:release
174002 lines; 9.050s; 655.375MiB peakmem

metagn added a commit to metagn/Nim that referenced this pull request Sep 3, 2024
Araq pushed a commit that referenced this pull request Sep 4, 2024
Araq pushed a commit that referenced this pull request Sep 6, 2024
fixes #16700, fixes #20916, refs #24010

Fixes the instantiation issues for proc param default values encountered
in #24010 by:

1. semchecking generic default param values with `inGenericContext` for
#22029 and followups to apply (the bigger change in semtypes),
2. rejecting explicit generic instantiations with unresolved generic
types inside `inGenericContext` (sigmatch change),
3. instantiating the default param values using `prepareNode` rather
than an insufficient manual method (the bigger change in seminst).

This had an important side effect of references to other parameters not
working since they would be resolved as a symbol belonging to the
uninstantiated original generic proc rather than the later instantiated
proc. There is a more radical way to fix this which is generating ident
nodes with `tyFromExpr` in specifically this context, but instead we
just count them as belonging to the same proc in
`hoistParamsUsedInDefault`.

Other minor bugfixes:

* To make the error message in t20883 make sense, we now give a "cannot
instantiate" error when trying to instantiate a proc generic param with
`tyFromExpr`.
* Object constructors as default param values generated default values
of private fields going through `evalConstExpr` more than once, but the
VM doesn't mark the object fields as `nfSkipFieldChecking` which gives a
"cannot access field" error. So the VM now marks object fields it
generates as `nfSkipFieldChecking`. Not sure if this affects VM
performance, don't see why it would.
* The nkRecWhen changes in #24042 didn't cover the case where all
conditions are constantly false correctly, this is fixed with a minor
change. This isn't needed for this PR now but I encountered it after
forgetting to `dec c.inGenericContext`.
Araq pushed a commit that referenced this pull request Sep 19, 2024
refs #24010, refs
#24125 (comment)

The generic mismatch errors added in #24010 made it possible for `nArg`
to be `nil` in the error reporting since it checked the call argument
list, not the generic parameter list for the mismatching argument node,
which causes a segfault. This is fixed by checking the generic parameter
list immediately on any generic mismatch error.

Also the `typedesc` type is skipped for the value of the generic params
since it's redundant and the generic parameter constraints don't have
it.
metagn added a commit to metagn/Nim that referenced this pull request Sep 21, 2024
…#24140)

refs nim-lang#24010, refs
nim-lang#24125 (comment)

The generic mismatch errors added in nim-lang#24010 made it possible for `nArg`
to be `nil` in the error reporting since it checked the call argument
list, not the generic parameter list for the mismatching argument node,
which causes a segfault. This is fixed by checking the generic parameter
list immediately on any generic mismatch error.

Also the `typedesc` type is skipped for the value of the generic params
since it's redundant and the generic parameter constraints don't have
it.
Araq pushed a commit that referenced this pull request Oct 18, 2024
refs #8064, refs #24010

Error messages for standalone explicit generic instantiations are
revamped. Failing standalone explicit generic instantiations now only
error after overloading has finished and resolved to the default `[]`
magic (this means `[]` can now be overloaded for procs but this isn't
necessarily intentional, in #24010 it was documented that it isn't
possible). The error messages for failed instantiations are also no
longer a simple `cannot instantiate: foo` message, instead they now give
the same type mismatch error message as overloads with mismatching
explicit generic parameters.

This is now possible due to the changes in #24010 that delegate all
explicit generic proc instantiations to overload resolution. Old code
that worked around this is now removed. `maybeInstantiateGeneric` could
maybe also be removed in favor of just `explicitGenericSym`, the `result
== n` case is due to `explicitGenericInstError` which is only for niche
cases.

Also, to cause "ambiguous identifier" error messages when the explicit
instantiation is a symchoice and the expression context doesn't allow
symchoices, we semcheck the sym/symchoice created by
`explicitGenericSym` with the given expression flags.

#8064 isn't entirely fixed because the error message is still misleading
for the original case which does `values[1]`, as a consequence of
#12664.
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.

explicitly instantiated generic template fails when it has a proc overload
2 participants