@@ -30,11 +30,12 @@ Build function definitions for a module's builtin facets.
3030private def Module.recFetchInput (mod : Module) : FetchM (Job ModuleInput) := Job.async do
3131 let path := mod.leanFile
3232 let contents ← IO.FS.readFile path
33- setTrace {caption := path.toString, mtime := ← getMTime path, hash := .ofText contents}
33+ let trace := {caption := path.toString, mtime := ← getMTime path, hash := .ofText contents}
34+ setTrace trace
3435 let header ← Lean.parseImports' contents path.toString
3536 let imports ← header.imports.mapM fun imp => do
3637 return ⟨imp, (← findModule? imp.module)⟩
37- return {path, header, imports}
38+ return {path, header, imports, trace }
3839
3940/-- The `ModuleFacetConfig` for the builtin `inputFacet`. -/
4041public def Module.inputFacetConfig : ModuleFacetConfig inputFacet :=
@@ -261,25 +262,37 @@ private def ModuleImportInfo.nil (modName : Name) : ModuleImportInfo where
261262 allTransTrace := .nil s! "{ modName} transitive imports (all)"
262263 legacyTransTrace := .nil s! "{ modName} transitive imports (legacy)"
263264
265+ private def ModuleExportInfo.disambiguationHash
266+ (self : ModuleExportInfo) (nonModule : Bool) (imp : Import)
267+ : Hash :=
268+ if nonModule then
269+ self.legacyTransTrace.hash.mix self.allArtsTrace.hash
270+ else if imp.importAll then
271+ self.allTransTrace.hash.mix self.allArtsTrace.hash
272+ else if imp.isMeta then
273+ self.metaTransTrace.hash.mix self.metaArtsTrace.hash
274+ else
275+ self.transTrace.hash.mix self.artsTrace.hash
276+
264277private def ModuleImportInfo.addImport
265278 (info : ModuleImportInfo) (nonModule : Bool)
266- (mod : Module) ( imp : Import) (expInfo : ModuleExportInfo)
279+ (imp : Import) (expInfo : ModuleExportInfo)
267280: ModuleImportInfo :=
268281 let info :=
269282 if nonModule then
270283 {info with
271- directArts := info.directArts.insert mod.name expInfo.allArts
284+ directArts := info.directArts.insert imp.module expInfo.allArts
272285 trace := info.trace.mix expInfo.legacyTransTrace |>.mix expInfo.allArtsTrace.withoutInputs
273286 }
274287 else if imp.importAll then
275288 {info with
276- directArts := info.directArts.insert mod.name expInfo.allArts
289+ directArts := info.directArts.insert imp.module expInfo.allArts
277290 trace := info.trace.mix expInfo.allTransTrace |>.mix expInfo.allArtsTrace.withoutInputs
278291 }
279292 else
280293 let info :=
281- if !info.directArts.contains mod.name then -- do not demote `import all`
282- {info with directArts := info.directArts.insert mod.name expInfo.arts}
294+ if !info.directArts.contains imp.module then -- do not demote `import all`
295+ {info with directArts := info.directArts.insert imp.module expInfo.arts}
283296 else
284297 info
285298 if imp.isMeta then
@@ -338,6 +351,12 @@ private def ModuleImportInfo.addImport
338351 else
339352 info
340353
354+ private def Package.discriminant (self : Package) :=
355+ if self.version == {} then
356+ self.name.toString
357+ else
358+ s! "{ self.name} @{ self.version} "
359+
341360private def fetchImportInfo
342361 (fileName : String) (pkgName modName : Name) (header : ModuleHeader)
343362: FetchM (Job ModuleImportInfo) := do
@@ -348,13 +367,51 @@ private def fetchImportInfo
348367 if modName = imp.module then
349368 logError s! "{ fileName} : module imports itself"
350369 return .error
351- let some mod ← findModule? imp.module
352- | return s
353- if imp.importAll && !mod.allowImportAll && pkgName != mod.pkg.name then
354- logError s! "{ fileName} : cannot 'import all' across packages"
355- return .error
356- let importJob ← mod.exportInfo.fetch
357- return s.zipWith (·.addImport nonModule mod imp ·) importJob
370+ let mods ← findModules imp.module
371+ let n := mods.size
372+ if h : n = 0 then
373+ return s
374+ else if n = 1 then -- common fast path
375+ let mod := mods[0 ]
376+ if imp.importAll && !mod.allowImportAll && pkgName != mod.pkg.name then
377+ logError s! "{ fileName} : cannot `import all` \
378+ the module `{ imp.module} ` from the package `{ mod.pkg.discriminant} `"
379+ return .error
380+ let importJob ← mod.exportInfo.fetch
381+ return s.zipWith (sync := true ) (·.addImport nonModule imp ·) importJob
382+ else
383+ let isImportable (mod) :=
384+ mod.allowImportAll || pkgName == mod.pkg.name
385+ let allImportable :=
386+ if imp.importAll then
387+ mods.all isImportable
388+ else true
389+ unless allImportable do
390+ let msg := s! "{ fileName} : cannot `import all` the module `{ imp.module} ` \
391+ from the following packages:"
392+ let msg := mods.foldl (init := msg) fun msg mod =>
393+ if isImportable mod then
394+ msg
395+ else
396+ s! "{ msg} \n { mod.pkg.discriminant} "
397+ logError msg
398+ return .error
399+ let mods : Vector Module n := .mk mods rfl
400+ let expInfosJob ← Job.collectVector <$> mods.mapM (·.exportInfo.fetch)
401+ s.bindM (sync := true ) fun impInfo => do
402+ expInfosJob.mapM (sync := true ) fun expInfos => do
403+ let expInfo := expInfos[0 ]
404+ let impHash := expInfo.disambiguationHash nonModule imp
405+ let allEquiv := expInfos.toArray.all (start := 1 ) fun expInfo =>
406+ impHash == expInfo.disambiguationHash nonModule imp
407+ unless allEquiv do
408+ let msg := s! "{ fileName} : could not disambiguate the module `{ imp.module} `; \
409+ multiple packages provide distinct definitions:"
410+ let msg := n.fold (init := msg) fun i h s =>
411+ let hash := expInfos[i].disambiguationHash nonModule imp
412+ s! "{ s} \n { mods[i].pkg.discriminant} (hash: { hash} )"
413+ error msg
414+ return impInfo.addImport nonModule imp expInfo
358415
359416/-- The `ModuleFacetConfig` for the builtin `importInfoFacet`. -/
360417public def Module.importInfoFacetConfig : ModuleFacetConfig importInfoFacet :=
@@ -374,20 +431,21 @@ private def noIRError :=
374431/-- Computes the import artifacts and transitive import trace of a module's imports. -/
375432private def Module.computeExportInfo (mod : Module) : FetchM (Job ModuleExportInfo) := do
376433 (← mod.leanArts.fetch).mapM (sync := true ) fun arts => do
377- let header ← (← mod.header .fetch).await
434+ let input ← (← mod.input .fetch).await
378435 let importInfo ← (← mod.importInfo.fetch).await
379436 let artsTrace := BuildTrace.nil s! "{ mod.name} :importArts"
380437 let metaArtsTrace := BuildTrace.nil s! "{ mod.name} :importArts (meta)"
381438 let allArtsTrace := BuildTrace.nil s! "{ mod.name} :importAllArts"
382439 let olean := arts.olean
383- if header.isModule then
440+ if input. header.isModule then
384441 let some oleanServer := arts.oleanServer?
385442 | error noServerOLeanError
386443 let some ir := arts.ir?
387444 | error noIRError
388445 let some oleanPrivate := arts.oleanPrivate?
389446 | error noPrivateOLeanError
390447 return {
448+ srcTrace := input.trace
391449 arts := .ofArray #[olean.path, ir.path, oleanServer.path]
392450 artsTrace := artsTrace.mix olean.trace
393451 metaArtsTrace := metaArtsTrace.mix olean.trace |>.mix ir.trace
@@ -401,6 +459,7 @@ private def Module.computeExportInfo (mod : Module) : FetchM (Job ModuleExportIn
401459 }
402460 else
403461 return {
462+ srcTrace := input.trace
404463 arts := ⟨#[olean.path]⟩
405464 artsTrace := artsTrace.mix olean.trace
406465 metaArtsTrace := metaArtsTrace.mix olean.trace
0 commit comments