Skip to content

Regression from v2.0.14/version-2-0 to version-2-2/devel with overload resolution with enum values #24782

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

Open
tersec opened this issue Mar 15, 2025 · 2 comments

Comments

@tersec
Copy link
Contributor

tersec commented Mar 15, 2025

Nim Version

Builds:

Nim Compiler Version 2.0.14 [Linux: amd64]
Compiled at 2025-03-15
Copyright (c) 2006-2023 by Andreas Rumpf

git hash: bf4de6a394e040d9810cba8c69fb2829ff04dcc6
active boot switches: -d:release
Nim Compiler Version 2.0.15 [Linux: amd64]
Compiled at 2025-03-15
Copyright (c) 2006-2023 by Andreas Rumpf

git hash: d01002d8f8cf2c12e67e4ddae3e5990a7d92e753
active boot switches: -d:release

Does not build:

Nim Compiler Version 2.2.3 [Linux: amd64]
Compiled at 2025-03-15
Copyright (c) 2006-2025 by Andreas Rumpf

git hash: 44c1b2a6df6bcf47188b84dcdaad704685f72904
active boot switches: -d:release
Nim Compiler Version 2.3.1 [Linux: amd64]
Compiled at 2025-03-15
Copyright (c) 2006-2025 by Andreas Rumpf

git hash: fb93295344b78d2d45c81bc78bdba8526a893a09
active boot switches: -d:release

Description

k.nim:

import ./e
import ./w

proc r(_: int|int) = discard J()
r(0)

e.nim:

type J* = object

w.nim:

type V* = enum
  J

Notably, this isn't truly ambiguous: if one tries to actually use discard w.J() to resolve it to that option, one gets:

/tmp/k.nim(5, 2) template/generic instantiation of `r` from here
/tmp/k.nim(4, 33) Error: attempting to call routine: 'J'
  found 'J' [enumField declared in /tmp/w.nim(2, 3)]

because indeed it isn't a routine. As https://nim-lang.org/docs/manual.html#types-enumeration-types points out:

Enum value names are overloadable, much like routines. If both of the enums T and U have a member named foo, then the identifier foo corresponds to a choice between T.foo and U.foo. During overload resolution, the correct type of foo is decided from the context. If the type of foo is ambiguous, a static error will be produced.

But the context here requires a routine, i.e.

a symbol of kind: proc, func, method, iterator, macro, template, converter
as it figures out with the static error Error: attempting to call routine.

So no actual ambiguity exists here.

Current Output

/tmp/k.nim(5, 2) template/generic instantiation of `r` from here
/tmp/k.nim(4, 31) Error: attempting to call routine: 'J'
  found e.J [type declared in /tmp/e.nim(1, 6)]
  found 'J' [enumField declared in /tmp/w.nim(2, 3)]

Expected Output


Known Workarounds

No response

Additional Information

No response

@metagn
Copy link
Collaborator

metagn commented Mar 15, 2025

Rules above are referring to overload resolution, which type constructors are separate from. The current behavior which is not well laid out in the compiler is: the first encountered symbol for the callee is checked; routine symbols start overload resolution, type symbols if they are not ambiguous (why this is failing now) are treated as type constructors/conversions, non-routine and non-type symbols end up in semIndirectOp which further checks for dot fields for UFCS/proc typed variables etc until finally overload resoution starts, which ignores type symbols.

We could easily make the "first encountered symbol for the callee" filter out things like enum fields since they can't be called anyway so that the type is not considered ambiguous here. But this can't always be lexically decided for things like proc variables:

# a.nim
type Foo* = object
# b.nim

var Foo*: int
# or
var Foo*: proc(): int
import a, b
let x = Foo()

In this case var Foo*: int would have to be ignored lexically but var Foo*: proc(): int cannot, it causes an ambiguity.

So we can either special case the symbol resolution here essentially just to exclude enum fields or via a filter that also checks the types, or we could include type constructors/conversions in the overload resolution step somehow as has been talked about before but adds significant complexity.

@Araq
Copy link
Member

Araq commented Mar 15, 2025

Once we have different "sum types" enum fields might in fact become type constructors and then E() becomes valid syntax and overload resolution couldn't filter it out...

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

No branches or pull requests

3 participants