Skip to content

Conversation

@plajjan
Copy link
Contributor

@plajjan plajjan commented Oct 8, 2025

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:

  • Extract importWitsFromModule as a reusable function to import witnesses from a single module's type environment
  • Modify importWits to be IO-based and process transitive dependencies by reading .ty files and importing witnesses from dependencies that are already loaded in the environment
  • Call importWitsFromModule in subImp to ensure witnesses are imported as each transitive dependency is loaded

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.

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:

- Extract importWitsFromModule as a reusable function to import
  witnesses from a single module's type environment
- Modify importWits to be IO-based and process transitive dependencies
  by reading .ty files and importing witnesses from dependencies that
  are already loaded in the environment
- Call importWitsFromModule in subImp to ensure witnesses are imported
  as each transitive dependency is loaded

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants