-
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
Start adding lazy import references to name lookup. #3475
Conversation
219a679
to
376c1a9
Compare
Noticed while working on #3475, but felt like it would be easy enough to separate out.
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.
[No action required for this comment, just musings.] Thinking about the model here, suppose we have a chain of imports:
A <- B <- C <- ...
The gold standard would be that if A adds a public declaration that B doesn't use, then the output for B doesn't change at all. That way the build system can stop rebuilding dependents at that point. What I think happens here is:
- A rebuilds and now has one more export.
- B rebuilds and now has one more (unused) lazy import decl.
- C rebuilds and its lazy import decls from B now have different instruction IDs in them, because everything in B got renumbered.
- D rebuilds but doesn't change.
So we do two extra levels of rebuilds beyond what's strictly necessary before we quiesce. I think this is completely fine for now, but might be something we'll want to take another look at later (post-1.0) to reduce transitive rebuilds. (Perhaps this will turn out to be a non-issue anyway: if we only serialize the things that actually end up being used / reachable from public names, I think that would solve the problem, and we might want to do that regardless.)
// The previous state of name_lookup_has_load_error_, restored on pop. | ||
bool prev_name_lookup_has_load_error; |
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.
Why do we use a backup / restore approach here, rather than always looking at the state on the top of the scope stack when querying name_lookup_has_load_error_
? (I'm not opposed to this if there's a good reason, but I think maintaining this flag alongside other current-scope information like the names
set seems simpler if either would work.)
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.
I don't think that'd work:
import library "broken";
class C {
fn Foo() {
// Top of the scope stack has no load error, but this does name lookup into the package.
var a: i32 = y;
}
// This pops the scope to `C`, `C`'s name scope does not have a load error, but
// this still shouldn't print because it's unqualified name lookup.
var b: i32 = y;
}
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.
The idea would be that when you push a new scope, its "name lookup has load error" flag is set to that of the enclosing scope.
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.
LGTM once comments are considered; the only one I consider blocking is the missing FormatInstructionLHS
/ name printing for lazy imports.
I'm not going to worry about this too much right now. I would expect a focus on trying to reduce transitive rebuilds, but I'd also expect transitive (*there may also be fun tricks in serialization to replace the inst id) |
Adds a
LazyImportRef
instruction. VersusCrossRef
, this is intended to represent an instruction which cannot be used directly, and must be replaced when it comes up due to name lookup. The intent is to use this to avoid recursive loading of imported IR instructions.Note, under this model, when
ResolveIfLazyImportRef
is called, it essentially needs to load both inst and type information to a sufficient point where any further attempts would hit name lookup again. That will probably be complex, and the current implementation is just touching the surface of the issue. I was heading down this route because it would mean we have a limited number of points that need to consider whether they're going to talk about aLazyImportRef
.I'm considering whether
CrossRef
should be dropped in favor of more specificBuiltin
special-casing, due to the divergence of desired behaviors. This could mean dropping thebuiltins
IR since it's not looking useful right now.Modify
NameScope
to track whether the scope is associated with a load error. This is to handle cases where one or more imports failed, so we do not want to issue warnings for related scopes.The 0-size on
ValueStore
comes up due to the changes toNameScope
, which make it too large for the default handling. After discussion with zygoloid, the thought was we might want to try reserving a roughly correct value based on parse node counts, but the stack default wasn't buying much.Fixes a bug where the implicit import used the package name instead of the invalid identifier.