-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
implement type bound operation RFC #24315
Conversation
It's better to do it lazily. If |
implements nim-lang/RFCs#380
How does this affect compile times? |
Temporarily enabled the option globally to see since new code only runs with it on now. Also tests that the new changes work.
The performance in There are a few possible optimizations I can think of but they'll add complexity for little gain:
|
Sure but it's easy to measure: bootstrap time with type bound ops disabled: 5.2s, with enabled: 5.3s (for example!) |
Ran Times with experimental switch off:
Times with it on:
So 5.297 ± 0.118 seconds with it off and 5.459 ± 0.365 seconds with it on. Removing the 2 worst and 2 best times for each, it gives 5.294 ± 0.059 vs 5.380 ± 0.159. That is about 90 milliseconds or a 2% impact at worst but other codebases might be impacted more than the compiler. The bootstrap times in the CI seem to have a similar difference. |
nimbus-eth2/beacon_chain/nimbus_beacon_node takes several minutes to compile, if you're looking for a compiler speed stress test - it's quite slow and not getting any faster |
#result = nominalRoot(t.skipModifier) | ||
result = nil | ||
of tyStatic: | ||
# ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a follow-up PR we should treat static
just like var
and sink
here.
Thanks for your hard work on this PR! Hint: mm: orc; opt: speed; options: -d:release |
closes nim-lang/RFCs#380, fixes #4773, fixes #14729, fixes #16755, fixes #18150, fixes #22984, refs #11167 (only some comments fixed), refs #12620 (needs tiny workaround)
The compiler gains a concept of root "nominal" types (i.e. objects, enums, distincts, direct
Foo = ref object
s, generic versions of all of these). Exported top-level routines in the same module as the nominal types that their parameter types derive from (i.e. withvar
/sink
/typedesc
/generic constraints) are considered attached to the respective type, as the RFC states. This happens for every argument regardless of placement.When a call is overloaded and overload matching starts, for all arguments in the call that already have a type, we add any operation with the same name in the scope of the root nominal type of each argument (if it exists) to the overload match. This also happens as arguments gradually get typed after every overload match. This restricts the considered overloads to ones attached to the given arguments, as well as preventing
untyped
arguments from being forcefully typed due to unrelated overloads. There are some caveats:foo
is not declared,foo(x)
will not consider a type bound op forx
.foo()
is in scope,foo(x)
will not consider a type bound op forx
.In the cases of "generic interfaces" like
hash
,$
,items
etc. this is not really a problem since any code using it will have at least one typed overload imported. For arbitrary versions of these though, as in the test case for #12620, a workaround is to declare a temporary "template" overload that never matches:I don't know what a "proper" version of this could be, maybe something to do with the new concepts.
Possible directions:
A limitation with the proposal is that parameters like
a: ref Foo
are not attached to any type, even ifFoo
is nominal. Fixing this for justptr
/ref
would be a special case, parameters likeseq[Foo]
would still not be attached toFoo
. We could also skip any structural type but this could produce more than one nominal type, i.e.(Foo, Bar)
(not that this is hard to implement, it just might be unexpected).Converters do not use type bound ops, they still need to be in scope to implicitly convert. But maybe they could also participate in the nominal type consideration: if
Generic[T] = distinct T
has a converter toT
, bothGeneric
andT
can be considered as nominal roots.The other restriction in the proposal, being in the same scope as the nominal type, could maybe be worked around by explicitly attaching to the type, i.e.:
proc foo(x: T) {.attach: T.}
, similar to class extensions in newer OOP languages. The given typeT
needs to be obtainable from the type of the given argumentx
however, i.e. something likeproc foo(x: ref T) {.attach: T.}
doesn't work to fix theref
issue since the compiler never obtainsT
from a givenref T
argument. Edit: Since the module is queried now, this is likely not possible.