Skip to content

Commit 2fda359

Browse files
committed
go/packages: use export data to load types info
Use export data in LoadTypes mode, when it is available to get the type information. The lock can be moved back to loadFromExportData, as each package is loaded in topological order, and all calls to loadFromExportData for a particular package occur when that package is being loaded. Fixes golang/go#26834 Change-Id: Ib6c28eb8642a473cc100d54d0aac7b90644d5d22 Reviewed-on: https://go-review.googlesource.com/128365 Reviewed-by: Michael Matloob <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent 1bd7298 commit 2fda359

File tree

2 files changed

+140
-112
lines changed

2 files changed

+140
-112
lines changed

go/packages/packages.go

Lines changed: 34 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -128,19 +128,18 @@ type Config struct {
128128

129129
// TypeChecker provides additional configuration for type-checking syntax trees.
130130
//
131-
// The TypeCheck loader does not use the TypeChecker configuration
132-
// for packages that have their type information provided by the
133-
// underlying build system.
131+
// It is used for all packages in LoadAllSyntax mode,
132+
// and for the packages matching the patterns, but not their dependencies,
133+
// in LoadSyntax mode.
134134
//
135135
// The TypeChecker.Error function is ignored:
136136
// errors are reported using the Error function defined above.
137137
//
138138
// The TypeChecker.Importer function is ignored:
139139
// the loader defines an appropriate importer.
140140
//
141-
// The TypeChecker.Sizes are only used by the WholeProgram loader.
142-
// The TypeCheck loader uses the same sizes as the main build.
143-
// TODO(rsc): At least, it should. Derive these from runtime?
141+
// TODO(rsc): TypeChecker.Sizes should use the same sizes as the main build.
142+
// Derive them from the runtime?
144143
TypeChecker types.Config
145144
}
146145

@@ -232,7 +231,7 @@ type loaderPackage struct {
232231
importErrors map[string]error // maps each bad import to its error
233232
loadOnce sync.Once
234233
color uint8 // for cycle detection
235-
mark, needsrc bool // used in TypeCheck mode only
234+
mark, needsrc bool // used when Mode >= LoadTypes
236235
}
237236

238237
func (lpkg *Package) String() string { return lpkg.ID }
@@ -317,7 +316,8 @@ func (ld *loader) loadFrom(roots []string, list ...*raw.Package) ([]*Package, er
317316
GoFiles: pkg.GoFiles,
318317
OtherFiles: pkg.OtherFiles,
319318
},
320-
needsrc: ld.Mode >= LoadAllSyntax || pkg.Export == "",
319+
needsrc: ld.Mode >= LoadAllSyntax ||
320+
(pkg.Export == "" && pkg.PkgPath != "unsafe"),
321321
}
322322
ld.pkgs[lpkg.ID] = lpkg
323323
}
@@ -421,11 +421,10 @@ func (ld *loader) loadFrom(roots []string, list ...*raw.Package) ([]*Package, er
421421
return result, nil
422422
}
423423

424-
// loadRecursive loads, parses, and type-checks the specified package and its
425-
// dependencies, recursively, in parallel, in topological order.
424+
// loadRecursive loads the specified package and its dependencies,
425+
// recursively, in parallel, in topological order.
426426
// It is atomic and idempotent.
427-
// Precondition: ld.mode != Metadata.
428-
// In typeCheck mode, only needsrc packages are loaded.
427+
// Precondition: ld.Mode >= LoadTypes.
429428
func (ld *loader) loadRecursive(lpkg *loaderPackage) {
430429
lpkg.loadOnce.Do(func() {
431430
// Load the direct dependencies, in parallel.
@@ -444,11 +443,10 @@ func (ld *loader) loadRecursive(lpkg *loaderPackage) {
444443
})
445444
}
446445

447-
// loadPackage loads, parses, and type-checks the
448-
// files of the specified package, if needed.
446+
// loadPackage loads the specified package.
449447
// It must be called only once per Package,
450448
// after immediate dependencies are loaded.
451-
// Precondition: ld.mode != Metadata.
449+
// Precondition: ld.Mode >= LoadTypes.
452450
func (ld *loader) loadPackage(lpkg *loaderPackage) {
453451
if lpkg.raw.PkgPath == "unsafe" {
454452
// Fill in the blanks to avoid surprises.
@@ -459,8 +457,14 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
459457
return
460458
}
461459

460+
// Call NewPackage directly with explicit name.
461+
// This avoids skew between golist and go/types when the files'
462+
// package declarations are inconsistent.
463+
lpkg.Types = types.NewPackage(lpkg.raw.PkgPath, lpkg.Name)
464+
462465
if !lpkg.needsrc {
463-
return // not a source package
466+
ld.loadFromExportData(lpkg)
467+
return // not a source package, don't get syntax trees
464468
}
465469

466470
hardErrors := false
@@ -482,11 +486,6 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
482486
lpkg.Fset = ld.Fset
483487
lpkg.Syntax = files
484488

485-
// Call NewPackage directly with explicit name.
486-
// This avoids skew between golist and go/types when the files'
487-
// package declarations are inconsistent.
488-
lpkg.Types = types.NewPackage(lpkg.raw.PkgPath, lpkg.Name)
489-
490489
lpkg.TypesInfo = &types.Info{
491490
Types: make(map[ast.Expr]types.TypeAndValue),
492491
Defs: make(map[*ast.Ident]types.Object),
@@ -516,16 +515,9 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
516515
return nil, fmt.Errorf("no metadata for %s", path)
517516
}
518517

519-
ld.exportMu.Lock()
520-
defer ld.exportMu.Unlock()
521-
522518
if ipkg.Types != nil && ipkg.Types.Complete() {
523519
return ipkg.Types, nil
524520
}
525-
imp := ld.pkgs[ipkg.ID]
526-
if !imp.needsrc {
527-
return ld.loadFromExportData(imp)
528-
}
529521
log.Fatalf("internal error: nil Pkg importing %q from %q", path, lpkg)
530522
panic("unreachable")
531523
})
@@ -643,16 +635,8 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
643635
// Not all accesses to Package.Pkg need to be protected by exportMu:
644636
// graph ordering ensures that direct dependencies of source
645637
// packages are fully loaded before the importer reads their Pkg field.
646-
//
647-
// TODO(matloob): Ask adonovan if this is safe. Because of diamonds in the
648-
// dependency graph, it's possible that the same package is loaded in
649-
// two separate goroutines and there's a race in the write of the Package's
650-
// Types here and the read earlier checking if types is set before calling
651-
// this function.
652-
/*
653-
ld.exportMu.Lock()
654-
defer ld.exportMu.Unlock()
655-
*/
638+
ld.exportMu.Lock()
639+
defer ld.exportMu.Unlock()
656640

657641
if tpkg := lpkg.Types; tpkg != nil && tpkg.Complete() {
658642
return tpkg, nil // cache hit
@@ -690,45 +674,28 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
690674
//
691675
// So, we must build a PkgPath-keyed view of the global
692676
// (conceptually ID-keyed) cache of packages and pass it to
693-
// gcexportdata, then copy back to the global cache any newly
694-
// created entries in the view map. The view must contain every
695-
// existing package that might possibly be mentioned by the
696-
// current package---its reflexive transitive closure.
697-
//
698-
// (Yes, reflexive: although loadRecursive processes source
699-
// packages in topological order, export data packages are
700-
// processed only lazily within Importer calls. In the graph
701-
// A->B->C, A->C where A is a source package and B and C are
702-
// export data packages, processing of the A->B and A->C import
703-
// edges may occur in either order, depending on the sequence
704-
// of imports within A. If B is processed first, and its export
705-
// data mentions C, an incomplete package for C will be created
706-
// before processing of C.)
707-
// We could do export data processing in topological order using
708-
// loadRecursive, but there's no parallelism to be gained.
677+
// gcexportdata. The view must contain every existing
678+
// package that might possibly be mentioned by the
679+
// current package---its transitive closure.
709680
//
710681
// TODO(adonovan): it would be more simpler and more efficient
711682
// if the export data machinery invoked a callback to
712683
// get-or-create a package instead of a map.
713684
//
714685
view := make(map[string]*types.Package) // view seen by gcexportdata
715686
seen := make(map[*loaderPackage]bool) // all visited packages
716-
var copyback []*loaderPackage // candidates for copying back to global cache
717-
var visit func(p *loaderPackage)
718-
visit = func(p *loaderPackage) {
719-
if !seen[p] {
720-
seen[p] = true
721-
if p.Types != nil {
722-
view[p.raw.PkgPath] = p.Types
723-
} else {
724-
copyback = append(copyback, p)
725-
}
726-
for _, p := range p.Imports {
727-
visit(ld.pkgs[p.ID])
687+
var visit func(pkgs map[string]*Package)
688+
visit = func(pkgs map[string]*Package) {
689+
for _, p := range pkgs {
690+
lpkg := ld.pkgs[p.ID]
691+
if !seen[lpkg] {
692+
seen[lpkg] = true
693+
view[lpkg.raw.PkgPath] = lpkg.Types
694+
visit(lpkg.Imports)
728695
}
729696
}
730697
}
731-
visit(lpkg)
698+
visit(lpkg.Imports)
732699

733700
// Parse the export data.
734701
// (May create/modify packages in view.)
@@ -737,12 +704,6 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
737704
return nil, fmt.Errorf("reading %s: %v", lpkg.raw.Export, err)
738705
}
739706

740-
// For each newly created types.Package in the view,
741-
// save it in the main graph.
742-
for _, p := range copyback {
743-
p.Types = view[p.raw.PkgPath] // may still be nil
744-
}
745-
746707
lpkg.Types = tpkg
747708
lpkg.IllTyped = false
748709

0 commit comments

Comments
 (0)