Add transitive witness propagation for protocol extensions #2470
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Protocol extensions (witnesses) provide method implementations for protocols. Previously, witnesses were only imported from directly imported modules. This meant that when module m3 imports m2, and m2 imports m1 (where m1 has an extension making Thing implement Iterable), m3 couldn't use Thing as an Iterable without explicitly importing m1.
This change enhances witness importing to handle transitive dependencies:
The key insight is to check if dependencies are loaded in the environment (via lookupMod) rather than requiring them to be in the imports list. This allows protocol implementations to properly propagate through module dependency chains.
Before: When compiling m3 which imports m2, importWits would only add witnesses from m2's TEnv to m3's environment. Even though m1 was loaded into the module cache (via subImp when processing m2's dependencies), m1's witnesses were never imported because the old importWits only processed the module being directly imported.
Now: When compiling m3 which imports m2, subImp loads m1 (as m2's dependency) and immediately imports m1's witnesses via importWitsFromModule. Later, when importWits processes m2, it reads m2.ty to find m2's dependencies, sees m1 is already in the module cache, and imports m1's witnesses into the current environment. The subImp change ensures witnesses are available as soon as modules are loaded, while the importWits change ensures they propagate to modules that need them.