The minimal imports generated by -ddump-minimal-imports are sometimes not correct.
For example, it generates import Agda.Utils.List1 ( pattern (:|) ) correctly, but doesn't include the pattern keyword for Def in import qualified Agda.Syntax.Abstract as A ( QName, NameToExpr(nameToExpr), Expr(Con), Def ), where Def is defined in the Agda.Syntax.Abstract module as:
-- | Pattern synonym for regular DefpatternDef::QName->ExprpatternDefx=Def'xNoSuffix
This limits the utility of the feature.
Environment
GHC version used: 8.10.1
OS; MacOS Catalina
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Child items
...
Show closed items
Linked items
0
Link issues together to show that they're related or that one is blocking others.
Learn more.
and you can make quicker-install-bin for a 5m build.
Personally, I would try to work with the example files that I attached above at #18247 (comment 278739) which show the same behaviour without as much ceremony.
Thanks. I am building a test case based on your examples, and see how it manifests. I was just confused as I initially looked at the hackage Agda version, and did not see those, they are a recent addition.
Great. I have also investigated whether the issue might be with the dumpMinimalImports function because it prints things unqualified, which is what you need for import/export lists, of course, but printing things out with the flag that prints module names too didn't help. Still no pattern. Very peculiar.
I have also checked that the imported things really are recognised as PatSyns and they seem to be.
Making the exports explicit in the example doesn't seem to help either.
So I think the ModIFacemod_exports field just lists the name in an instance of
dataAvailInfo-- | An ordinary identifier in scope=AvailName-- | A type or class in scope---- The __AvailTC Invariant__: If the type or class is itself to be in scope,-- it must be /first/ in this list. Thus, typically:---- > AvailTC Eq [Eq, ==, \/=] []|AvailTCName-- ^ The name of the type or class[Name]-- ^ The available pieces of type or class,-- excluding field selectors.[FieldLabel]-- ^ The record fields of the type-- (see Note [Representing fields in AvailInfo]).
but the actual declaration is stored more fully later.
And this is processed for the minimal imports via the to_ie function in GHC.Rename.Names.getMinimalImports.
This calls to_ie_post_rn, which does not care about adding the correct IEWrappedName because after parsing it should not matter. For reference, we have
I think it might be as simple as merging to_ie_post_rn and to_ie_post_rn_var into a single function. They do not conflict in the cases they manage. The only worry is if we can have a isDataOcc name that is not a pattern.
Looks v promising! I guess the there might be some merit in finding whoever wrote the code originally and seeing why they wrote it as they did. I'll also plug this into my test harness and see if anything breaks. Agda makes a good fuzz test!
This comment may help think about the menagerie of possibilities:
-- Renaming exports lists is a minefield. Five different things can appear in-- children export lists ( T(A, B, C) ).-- 1. Record selectors-- 2. Type constructors-- 3. Data constructors-- 4. Pattern Synonyms-- 5. Pattern Synonym Selectors---- However, things get put into weird name spaces.-- 1. Some type constructors are parsed as variables (-.->) for example.-- 2. All data constructors are parsed as type constructors-- 3. When there is ambiguity, we default type constructors to data-- constructors and require the explicit `type` keyword for type-- constructors.---- This function first establishes the possible namespaces that an-- identifier might be in (`choosePossibleNameSpaces`).---- Then for each namespace in turn, tries to find the correct identifier-- there returning the first positive result or the first terminating-- error.
... and then are always seem to be exceptions for data families.
Note to self: the original to_ie_post_rn_var (which produces IEPattern or IEName)
handles the case where the imported thing has field selectors, none of which are
overloaded (DuplicateRecordFields) so we can import just the selectors, without
the enclosing datatype.
to_ie_post_rn (IEType or IEName) handled all other cases.
Since patterns don't always have field selectors, there is a problem.
Thanks, when you say "I think the correct solution is to dereference the Avails to the actual declarations" what do you have in mind more specifically? I can see that that the Avails are trying to preserve only essential info for Rn and beyond and so discard the interesting stuff in LIEs tells you how to print them, etc, which is what you need for plugins, etc. So you end up with Avail Names that you need to track back to the originals, which seems odd.
(from the second of the fragments that you generated above)
Looking at 9.7.3 in the 8.10.1 user guide suggests that the pattern keyword should only be used when the pattern is exported on its own, to avoid confusion with type constructors.
Pattern synonyms can be imported and exported through association with a type constructor (including with wildcard). In that case there should be no pattern.
So I can see why we have two to_ie_post_rn functions.
80to_ie::ModIface->AvailInfo->[IEGhcRn]81-- The main trick here is that if we're importing all the constructors82-- we want to say "T(..)", but if we're importing only a subset we want83-- to say "T(A,B,C)". So we have to find out what the module exports.84to_ie_(Availn)85=[IEVarnoExtField(to_ie_post_rn_var$noLocn)]86-- = [IEVar noExtField (to_ie_post_rn $ noLoc n)]87to_ie_(AvailTCn[m][])88|n==m=[IEThingAbsnoExtField(to_ie_post_rn$noLocn)]89to_ieiface(AvailTCnnsfs)90=case[(xs,gs)|AvailTCxxsgs<-mi_exportsiface91,x==n92,x`elem`xs-- Note [Partial export]93]of94[xs]|all_usedxs->[IEThingAllnoExtField(to_ie_post_rn$noLocn)]95|otherwise->96[IEThingWithnoExtField(to_ie_post_rn$noLocn)NoIEWildcard97(map(to_ie_post_rn.noLoc)(filter(/=n)ns))98(mapnoLocfs)]99-- Note [Overloaded field import]100_other|all_non_overloadedfs101->map(IEVarnoExtField.to_ie_post_rn.noLoc)$ns102-- -> map (IEVar noExtField . to_ie_post_rn_var . noLoc) $ ns103++mapflSelectorfs104|otherwise->105[IEThingWithnoExtField(to_ie_post_rn$noLocn)NoIEWildcard106(map(to_ie_post_rn.noLoc)(filter(/=n)ns))107(mapnoLocfs)]108where109110fld_lbls=mapflLabelfs111112all_used(avail_occs,avail_flds)113=all(`elem`ns)avail_occs114&&all((`elem`fld_lbls).flLabel)avail_flds115116all_non_overloaded=not.anyflIsOverloaded117118to_ie_post_rn_var::(HasOccNamename)=>Locatedname->LIEWrappedNamename119to_ie_post_rn_var(Lln)120|isDataOcc$occNamen=Ll(IEPattern(Lln))121|otherwise=Ll(IEName(Lln))122123124to_ie_post_rn::(HasOccNamename)=>Locatedname->LIEWrappedNamename125to_ie_post_rn(Lln)126|isTcOccocc&&isSymOccocc=Ll(IEType(Lln))127|otherwise=Ll(IEName(Lln))128whereocc=occNamen
produces the first of your right examples above, without modding the to_ie_post_rns which is great.
I will have to study whether the IETypes are correct. For example, is the isSymOcc at line 126 right?
Another issue is that you get Patterns not matched: GHC.Hs.ImpExp.XImportDecl _ in relation to the decl in the body of mk_minimal.
Well, I've tweaked mk_minimal and it works better than the original, but isn't completely perfect. For example,
if I have a module List1 that imports Data.List.NonEmpty as List1 and import it into another module, where I use the :| operator, than that import is reduced to and import of (:|), rather than NonEmpty(:|) or pattern (:|). It works fine, if I import from Data.List.NonEmpty directly, rather than via List1
if I import a record type qualified and use it, and its fields, the fields don't all feature in the minimal imports.
I'll test this come more, but I'd be grateful if you could examine the logic, at least. Many thanks.
PS: (And while I remember, is there an easy way of telling whether an available export generated by exports_from_avail needs to be qualified, to distinguish it from an imported name?)
@alanz Alan, Well, I've kicked the tyres and not found any regressions in the approach above and it certainly removes cases where there were missing pattern / type annotations. There are, no doubt, still limitations, but they are no worse than they were before.
I have not fixed the warning
Pattern match(es) are non-exhaustive In a record-update construct: Patterns not matched: GHC.Hs.ImpExp.XImportDecl
for example and, of course, the serialization issue that I don't profess to understand remains.
Unless you have any better ideas, we could either leave it at that, or at least merge this version as an improvement, if not the final destination. What do you think?
74getMinimalImports::[ImportDeclUsage]->RnM[LImportDeclGhcRn]75getMinimalImports=mapMmk_minimal76where77mk_minimal(Lldecl,used_gres,unused)78|nullunused79,Just(False,_)<-ideclHidingdecl80=return(Lldecl)81|otherwise82=do{letImportDecl{ideclName=L_mod_name83,ideclSource=is_boot84,ideclPkgQual=mb_pkg}=decl85;iface<-loadSrcInterfacedocmod_nameis_boot(fmapsl_fsmb_pkg)86#ifMIN_VERSION_GLASGOW_HASKELL(8,8,0,0)87-- TODO not sure when this was introduced88;letused_avails=gresToAvailInfoused_gres89lies=map(Ll)(concatMap(to_ieiface)used_avails)90#else91-- used_gres are actually already AvailInfo in earlier versions of92-- GHC93;letlies=map(Ll)(concatMap(to_ieiface)used_gres)94#endif95;return(Ll(decl{ideclHiding=Just(False,Lllies)}))}96where97doc=text"Compute minimal imports for"<+>pprdecl9899to_ie::ModIface->AvailInfo->[IEGhcRn]100-- The main trick here is that if we're importing all the constructors101-- we want to say "T(..)", but if we're importing only a subset we want102-- to say "T(A,B,C)". So we have to find out what the module exports.103to_ie_(Availn)-- An ordinary identifier (eg, var, data constructor)104=[IEVarnoExt(to_ie_post_rn_var$noLocn)]105to_ie_(AvailTCn[m][])-- type or class with absent () list106|n==m=[IEThingAbsnoExt(to_ie_post_rn_name$noLocn)]107to_ieiface(AvailTCnnsfs)108=case[(xs,gs)|AvailTCxxsgs<-mi_exportsiface109,x==n110,x`elem`xs-- Note [Partial export]111]of112-- class / type with methods / constructors s113[xs]|all_usedxs->[IEThingAllnoExt(to_ie_post_rn_name$noLocn)]-- (..)114115|isTcOcc(occNamen)->-- typeclass -- @class Functor ...116[IEThingWithnoExt(to_ie_post_rn_name$noLocn)NoIEWildcard117(map(to_ie_post_rn_varn.noLoc)(filter(/=n)ns))118(mapnoLocfs)]119-- Note [Overloaded field import]120121|otherwise->-- type constructor (ie, @data X =@)122[IEThingWithnoExt(to_ie_post_rn_name$noLocn)NoIEWildcard123(map(to_ie_post_rn_cname.noLoc)(filter(/=n)ns))124(mapnoLocfs)]125126-- record type127_other|all_non_overloadedfs128->map(IEVarnoExt.to_ie_post_rn_name.noLoc)$ns129++mapflSelectorfs130|otherwise->-- DuplicateRecordFields is applicable131[IEThingWithnoExt(to_ie_post_rn_name$noLocn)NoIEWildcard132(map(to_ie_post_rn_cname.noLoc)(filter(/=n)ns))133(mapnoLocfs)]134where135136fld_lbls=mapflLabelfs137138all_used(avail_occs,avail_flds)139=all(`elem`ns)avail_occs140&&all((`elem`fld_lbls).flLabel)avail_flds141142all_non_overloaded=not.anyflIsOverloaded143144145-- The following were originally one function. Splitting it out this way allows146-- different variants to be used in different contexts above. This seems to147-- work, but needs review. Eg, do we need to use an isSymOcc in some cases?148to_ie_post_rn_name::Locatedname->LIEWrappedNamename149to_ie_post_rn_name(Lln)=Ll(IEName(Lln))150151to_ie_post_rn_var::(HasOccNamename)=>Locatedname->LIEWrappedNamename152to_ie_post_rn_var(Lln)153|isDataOcc$occNamen=Ll(IEPattern(Lln))154|otherwise=Ll(IEName(Lln))155156to_ie_post_rn_varn::(HasOccNamename)=>Locatedname->LIEWrappedNamename157to_ie_post_rn_varn(Lln)158|isTcOcc$occNamen=Ll(IEType(Lln))159|otherwise=Ll(IEName(Lln))160161to_ie_post_rn_cname::(HasOccNamename)=>Locatedname->LIEWrappedNamename162to_ie_post_rn_cname(Lln)163|isTcOcc$occNamen=Ll(IEType(Lln))164|otherwise=Ll(IEName(Lln))165166-- Notes167--168-- https://gitlab.haskell.org/ghc/ghc/-/wikis/pattern-synonyms/associating-synonyms169170-- An import is171-- a var172-- a tycls -> [(..) | ( var1 , … , varn )] -- class, etc173-- a tycon -> [ (..) | ( cname1 , … , cnamen )] -- data174175-- cname -> var | con176-- var -> varid | ( varsym ) -- (does not start with :)177-- con -> conid | ( consym ) -- (starts with :)178179-- GHC User Guide 8.7.3180-- The name of the pattern synonym is in the same namespace as proper data constructors.181-- Like normal data constructors, pattern synonyms can be imported through associations182-- with a type constructor or independently.183-- To export them *on their own*, in an export or import specification,184-- you must prefix pattern names with the pattern keyword185--186-- GHC User Guide 9.9.5187--The form C(.., mi, .., type Tj, ..), where C is a class, names the class C, and the188--specified methods mi and associated types Tj. The types need a keyword “type” to distinguish189--them from data constructors.190--191--Whenever there is no export list and a data instance is defined, the corresponding192--data family type constructor is exported along with the new data constructors, regardless of193--whether the data family is defined locally or in another module.194195-- isVarOcc -> variable name196-- isTvOcc -> is type variable197-- isTcOcc -> is type class name198-- isValOcc -- either in the variable or data constructor namespaces199-- isDataOcc -- Data constructor200-- isDataSymOcc -> Data contructuctor starting with a symbol201-- isSymOcc -> operator(data constructor, variable, etc)202--203-- So there are204-- var -> IEName205-- tycon -> can have IEPattern206-- tyclas -> can have IEType type (:+:)