You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
bpart: Redesign representation of implicit imports (#57755)
When we explicitly import a binding (via either `using M: x` or
`import`), the corresponding bpart representation is a direct pointer to
the imported binding. This is necessary, because that is the precise
semantic representation of the import.
However, for implicit imports (those created via `using M` without
explicit symbol name), the situation is a bit different. Here, there is
no semantic content to the particular binding being imported. Worse, the
binding is not necessarily unique. Our current semantics are essentially
that we walk the entire import graph (both explicit and implicit edges)
and if all reachable terminal nodes are either (i) the same binding or
(ii) constant bindings with the same value, the import is allowed. In
this, we are supposed to ignore cycles, although the current
implementation has some trouble with this (#57638, #57699).
If the import succeeds, in the current implementation, we then record an
arbitrary implicit import edge as in the BindingPartition. In essence,
we are creating a spanning tree of the import graph (formed from both
the implicit and explicit import edges). However, dynamic algorithms for
spanning tree maintenance are complicated and we didn't implement any.
As a result, it is possible for later edge additions to accidentally
introduce cycles (causing #57699).
An additional problem, even if we could keep a consistent spanning tree,
is that the spanning tree is not unique. In practice, this is not
supposed to be observable, but it is still odd to have a non-unique
representation for a particular set of imports. That said, we don't
really need the spanning tree. The only place that actually uses it is
`which(::Module, ::Symbol)` which is not performance sensitive and
arguably a bad API for the above reason that the answer is ill-defined.
With all these problems, let's just get rid of the spanning tree all
together - as mentioned we don't really need it. Instead, we split the
PARTITION_KIND_IMPLICIT into two, one for each of the two cases
(importing a global binding, or importing a particular constant value).
This is actually a more efficient implementation for the common case of
needing to follow a lookup - we no longer need to follow all the import
edges. In exchange, more work needs to be done during binding
replacement to re-scan the entire import graph. This is probably the
right trade-off though, since binding replacement is already a slow
path.
Fixes#57638, fixes#57699, fixes#57641, fixes#57700, fixes#57343
(cherry picked from commit 60a9f69)
0 commit comments