Open bpfoley opened 4 years ago
Comment from amfv:
I think this would be painful to do with qualified imports, because bsc makes the assumption that it can reach across package boundaries using explicit qualification in many places.
However, I think there might be a shortcut for the more common case of selective unqualified imports. I think it the compiler makes up on-the-fly a new package that imports the original package and exports only the selected identifiers, then the original package will be available (qualified) as other parts of bsc expects and only the selected identifiers will be added to the symbol table unqualified.
it the compiler makes up on-the-fly a new package
It doesn't need to be that complicated. BSC has a symbol table that defines whether a name is in scope: only keys in the map are in scope. When compiling package A that imports package B, BSC starts with an empty symbol table and populates it with the names from package B (inserting first the qualified name and then the unqualified name). Then BSC adds the names defined in package A, first the qualified and then the unqualified. If package A defines a name that shadows a name also in B, that is implemented by overwriting B's entry in the symbol table.
If package B is imported qualified, that is implemented by only inserting the qualified names into the table. That decision is made for the whole package, but BSC could easily filter individual names.
It would also be easy to add the qualified names with a renamed qualifier.
Not inserting the original qualified name may not be possible, though, as amfv notes:
I think this would be painful to do with qualified imports, because bsc makes the assumption that it can reach across package boundaries using explicit qualification in many places.
This can be seen in the following situation: If package A only imports B, and B imports C, then code in package A can refer to code in package C by qualifying the name (C::var
in BSV or C.var
in BH/Classic). This should not be allowed! But there is (or was?) a need for the qualified names to be available. And so, when constructing the symbol table, BSC not only inserts package B's names (qualified and unqualified) but also inserts all of package C's names (qualified only). Similarly, if package A wanted to hide a name from B when importing it, BSC could respect that by not inserting the unqualified name into the table, but BSC may still need to insert the qualified name, which would not be hidden.
I'd like to understand what needs this assumption, though. Does anything actually need it prior to elaboration? The elaboration process needs a symbol table that maps names (all qualified at this point) to their definitions, and it needs to contain the names from all packages in the dependency tree, because references from A to imported B names will be inlined with code from B that may contain references to C names.
But prior to elaboration, during the typecheck stage, what prevents BSC from using a symbol table that only has the names in scope for package A? Could it be that it was needed at one time, but is no longer needed? Also, I recall that BSC rebuilds the symbol table a few times, in those early stages (or did at one time), so I'd want to look up what's going on there -- and whether it adds further complications.
Bluespec allows packages to be imported qualified or unqualified, but doesn't allow packages to be renamed or symbols from a package to be hidden.
These imports are possible
Haskell allows considerably more complex imports with a variety of qualification, renaming, hiding and selective imports as can be seen at https://wiki.haskell.org/Import
A subset of these features would definitely be useful.