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
x/tools/go/packages: Consider giving consumers control over type-checking behaviour #66603
Comments
That's true, and it's a little fiddly, but it's not an insurmountable problem. For example, you can discard "unused import" errors in files with discarded function bodies, and if they are the only errors, you can consider the package well typed.
That seems reasonable, but I wonder exactly what problem you are trying to solve, because there may be a solution using the existing API. For example, if you're discarding all function bodies in the package, do you need typed syntax at all? It might be that export data contains sufficient information.
We wanted something like this this from the outset, but never managed to implement it, and I think it may be infeasible to do compatibly at this stage. The main challenge is that later calls to |
Thank you for your prompt answer and sorry for the delay. I needed to check some details before answering but I never got the time to until recently.
This is an interesting workaround, but it's still much more work (and code complexity) than just setting an option. By the way, I noticed recently that
I am (re)writing a binding generator for a RPC system. The requirements are as follows:
For the first step, I need full typed syntax including function bodies, but just for the root packages. For the second step, selected types and methods might live in dependencies of the root packages, but export data suffices except for doc comments. The approach I had in mind initially was to type-check the whole dependency graph, but include function bodies only for the roots. However, the current API does not make this very easy. Now I came up with an approach that might actually be better: I load and fully typecheck the root set of packages, without dependencies. If I discover methods or types that belong to a dependency, I load that package in The only issues I'm still battling with are:
In order to do this manually I need to resolve identifiers in file scopes, but export data has package scopes only. Maybe a feature that could help here is an automated way to match export data to declaring syntax (returning an error in case of failures). I can surely implement this myself, but IMO it's not a sensible thing to keep in my codebase.
I was concerned about this too. However, after giving it some thought I concluded that the concern is unwarranted. A single call to Tools that need to be robust w.r.t. file system changes (e.g. gopls) must implement their own monitoring mechanisms. Non-interactive tools can just warn the user not to touch the filesystem concurrently. At most — assuming they manage to detect inconsistencies — they should just throw an error and stop. Therefore, I believe this is not actually an issue. Is my reasoning sound here? If not, could you please help me see how? |
I think your best bet is to use one of these three approaches:
The first approach is simpler but slower. The second and third are faster because they avoid complete typed syntax for dependencies. (I don't have a good intuition about which is faster: perhaps the third one since it doesn't involve the compiler?) I don't think any of them should be hard to implement though. |
So you're suggesting I should invoke the type checker myself; then I would be able to control its configuration. I think I'll experiment some more with alternatives, then I may either go for that or convince people to relax requirements – we'll see. Thank you anyways. |
No, that's not necessary. |
What does "all instantiations of a certain generic function" mean in your context? Do you need only instantiations from non-parameterized code? Or type instantiations from within instantiations the bodies of generic function? |
Sorry, I misinterpreted
In the beginning I attempted using So I resorted to invoking However, I was concerned that this approach might not be robust enough when symbolic links or the overlay build flag are involved. This is partly what pushed me to look for alternatives and eventually open this issue. And again, this approach drives up code complexity a lot, has greater fragility, and it is a bit absurd considering that the type checker knows perfectly well how to ignore function bodies, and If that is the only alternative, it makes much more sense to me to invoke the type-checker manually. |
Ideally, I'd need instantiations from within instantiations of other generic functions too. As long as those too occur within the root set of packages. |
Compilers and x/tools/go/ssa + ssa.InstantiateGenerics are the only tools I am aware of that do this. This is a hard problem. You will need the ast bodies of all of the generic functions that may be reachable wand instantiate what those call. I would start by using x/tools/go/ssa or reading its source code. (You could also relax the goal.)
What does within the root set of packages mean? There are two parts. The site of the instantiation and the function being instantiated. So are you hoping for the site of the instantiation to be in the root package? The instantiated function is in the root package? Both? (FWIW when the set of root packages is all of the packages, this turns into ssa.InstantiateGenerics.) Also do you know that LoadAllSyntax cannot work for you for some reason? It may be the path of least resistance and save you time. (Of course x/tools/go/packages could always be better too...) |
You're not the only one. The mode bits are both poorly named and hard to use. (See #63517, #56677, #56633, and my tirade at #48226 (comment).)
It sounds like you're attempting to enumerate the complete set of instantiations required by a particular program, which is inherently a whole-program analysis, meaning that it must start from one or more main functions (in main packages). It can't entirely discard function bodies of dependencies, because they are needed in order to record that (for example) if you instantiate function
Yeah, that is unfortunate. |
Thank you both for your answers.
My search for a lighter approach was driven by two considerations:
I've run some experiments and here's what I found out:
At this point I am seriously considering a switch to However, the difference in performance is significant and I still would like very much to see an API for type-checker configuration added to |
The doc comment for the field
packages.Config.ParseFile
says:However, doing so (even when preserving semantic correctness) easily results in a bunch of errors being emitted about unused imports and other global identifiers.
Since the type-checker has an option
types.Config.IgnoreFuncBodies
that controls whether function bodies are checked, it would be nice if consumers of the package loader were able to configure that either per package (ideally) or at least globally.At the moment, the flag is only used internally for dependencies when NeedDeps is not set.
Another nice addition would be the ability to have distinct but overlapping calls to
packages.Load
(with the same config) share the same type-checkingObject
s (see #61418, #61412 for related, but different issues) and maybe even the same package graph. Such a feature would allow incremental, on-demand refinement of package information while still keeping the ability to reason about types globally, and avoiding duplicated work.The text was updated successfully, but these errors were encountered: