gopls/internal/lsp/cache: use persistent.Set in a couple places

Use the new persistent.Set type to track known dirs and unloadable
files.

Change-Id: I3e0d4bdc846f4c37a0046a01bf67d83bc06b9598
Reviewed-on: https://go-review.googlesource.com/c/tools/+/524762
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
Auto-Submit: Robert Findley <rfindley@google.com>
diff --git a/gopls/internal/lsp/cache/maps.go b/gopls/internal/lsp/cache/maps.go
index 3fa866c..edb72d5 100644
--- a/gopls/internal/lsp/cache/maps.go
+++ b/gopls/internal/lsp/cache/maps.go
@@ -76,46 +76,3 @@
 	}
 	return overlays
 }
-
-type knownDirsSet struct {
-	impl *persistent.Map[span.URI, struct{}]
-}
-
-func newKnownDirsSet() knownDirsSet {
-	return knownDirsSet{
-		impl: new(persistent.Map[span.URI, struct{}]),
-	}
-}
-
-func (s knownDirsSet) Clone() knownDirsSet {
-	return knownDirsSet{
-		impl: s.impl.Clone(),
-	}
-}
-
-func (s knownDirsSet) Destroy() {
-	s.impl.Destroy()
-}
-
-func (s knownDirsSet) Contains(key span.URI) bool {
-	_, ok := s.impl.Get(key)
-	return ok
-}
-
-func (s knownDirsSet) Range(do func(key span.URI)) {
-	s.impl.Range(func(key span.URI, value struct{}) {
-		do(key)
-	})
-}
-
-func (s knownDirsSet) SetAll(other knownDirsSet) {
-	s.impl.SetAll(other.impl)
-}
-
-func (s knownDirsSet) Insert(key span.URI) {
-	s.impl.Set(key, struct{}{}, nil)
-}
-
-func (s knownDirsSet) Remove(key span.URI) {
-	s.impl.Delete(key)
-}
diff --git a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/session.go
index cd51e6d..1e463fa 100644
--- a/gopls/internal/lsp/cache/session.go
+++ b/gopls/internal/lsp/cache/session.go
@@ -176,13 +176,13 @@
 		activePackages:       new(persistent.Map[PackageID, *Package]),
 		symbolizeHandles:     new(persistent.Map[span.URI, *memoize.Promise]),
 		workspacePackages:    make(map[PackageID]PackagePath),
-		unloadableFiles:      make(map[span.URI]struct{}),
+		unloadableFiles:      new(persistent.Set[span.URI]),
 		parseModHandles:      new(persistent.Map[span.URI, *memoize.Promise]),
 		parseWorkHandles:     new(persistent.Map[span.URI, *memoize.Promise]),
 		modTidyHandles:       new(persistent.Map[span.URI, *memoize.Promise]),
 		modVulnHandles:       new(persistent.Map[span.URI, *memoize.Promise]),
 		modWhyHandles:        new(persistent.Map[span.URI, *memoize.Promise]),
-		knownSubdirs:         newKnownDirsSet(),
+		knownSubdirs:         new(persistent.Set[span.URI]),
 		workspaceModFiles:    wsModFiles,
 		workspaceModFilesErr: wsModFilesErr,
 		pkgIndex:             typerefs.NewPackageIndex(),
@@ -619,15 +619,15 @@
 // knownDirectories returns all of the directories known to the given
 // snapshots, including workspace directories and their subdirectories.
 // It is responsibility of the caller to destroy the returned set.
-func knownDirectories(ctx context.Context, snapshots []*snapshot) knownDirsSet {
-	result := newKnownDirsSet()
+func knownDirectories(ctx context.Context, snapshots []*snapshot) *persistent.Set[span.URI] {
+	result := new(persistent.Set[span.URI])
 	for _, snapshot := range snapshots {
 		dirs := snapshot.dirs(ctx)
 		for _, dir := range dirs {
-			result.Insert(dir)
+			result.Add(dir)
 		}
 		knownSubdirs := snapshot.getKnownSubdirs(dirs)
-		result.SetAll(knownSubdirs)
+		result.AddAll(knownSubdirs)
 		knownSubdirs.Destroy()
 	}
 	return result
diff --git a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snapshot.go
index a914880..94eceed 100644
--- a/gopls/internal/lsp/cache/snapshot.go
+++ b/gopls/internal/lsp/cache/snapshot.go
@@ -130,7 +130,7 @@
 	shouldLoad map[PackageID][]PackagePath
 
 	// unloadableFiles keeps track of files that we've failed to load.
-	unloadableFiles map[span.URI]struct{}
+	unloadableFiles *persistent.Set[span.URI]
 
 	// TODO(rfindley): rename the handles below to "promises". A promise is
 	// different from a handle (we mutate the package handle.)
@@ -152,7 +152,7 @@
 
 	// knownSubdirs is the set of subdirectory URIs in the workspace,
 	// used to create glob patterns for file watching.
-	knownSubdirs      knownDirsSet
+	knownSubdirs      *persistent.Set[span.URI]
 	knownSubdirsCache map[string]struct{} // memo of knownSubdirs as a set of filenames
 	// unprocessedSubdirChanges are any changes that might affect the set of
 	// subdirectories in the workspace. They are not reflected to knownSubdirs
@@ -269,6 +269,7 @@
 	s.modTidyHandles.Destroy()
 	s.modVulnHandles.Destroy()
 	s.modWhyHandles.Destroy()
+	s.unloadableFiles.Destroy()
 }
 
 func (s *snapshot) SequenceID() uint64 {
@@ -750,7 +751,7 @@
 	}
 
 	// Check if uri is known to be unloadable.
-	_, unloadable := s.unloadableFiles[uri]
+	unloadable := s.unloadableFiles.Contains(uri)
 
 	s.mu.Unlock()
 
@@ -803,7 +804,7 @@
 	// so if we get here and still have
 	// no IDs, uri is unloadable.
 	if !unloadable && len(ids) == 0 {
-		s.unloadableFiles[uri] = struct{}{}
+		s.unloadableFiles.Add(uri)
 	}
 
 	// Sort packages "narrowest" to "widest" (in practice:
@@ -1017,14 +1018,14 @@
 	defer s.mu.Unlock()
 
 	s.knownSubdirs.Destroy()
-	s.knownSubdirs = newKnownDirsSet()
+	s.knownSubdirs = new(persistent.Set[span.URI])
 	s.knownSubdirsCache = nil
 	s.files.Range(func(uri span.URI, fh source.FileHandle) {
 		s.addKnownSubdirLocked(uri, dirs)
 	})
 }
 
-func (s *snapshot) getKnownSubdirs(wsDirs []span.URI) knownDirsSet {
+func (s *snapshot) getKnownSubdirs(wsDirs []span.URI) *persistent.Set[span.URI] {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 
@@ -1075,7 +1076,7 @@
 		if s.knownSubdirs.Contains(uri) {
 			break
 		}
-		s.knownSubdirs.Insert(uri)
+		s.knownSubdirs.Add(uri)
 		dir = filepath.Dir(dir)
 		s.knownSubdirsCache = nil
 	}
@@ -1592,7 +1593,7 @@
 	s.mu.Lock()
 	loadable := files[:0]
 	for _, file := range files {
-		if _, unloadable := s.unloadableFiles[file.URI()]; !unloadable {
+		if !s.unloadableFiles.Contains(file.URI()) {
 			loadable = append(loadable, file)
 		}
 	}
@@ -1655,7 +1656,7 @@
 		// metadata graph that resulted from loading.
 		uri := file.URI()
 		if len(s.meta.ids[uri]) == 0 {
-			s.unloadableFiles[uri] = struct{}{}
+			s.unloadableFiles.Add(uri)
 		}
 	}
 
@@ -1969,7 +1970,7 @@
 		files:                s.files.Clone(),
 		symbolizeHandles:     s.symbolizeHandles.Clone(),
 		workspacePackages:    make(map[PackageID]PackagePath, len(s.workspacePackages)),
-		unloadableFiles:      make(map[span.URI]struct{}, len(s.unloadableFiles)),
+		unloadableFiles:      s.unloadableFiles.Clone(), // see the TODO for unloadableFiles below
 		parseModHandles:      s.parseModHandles.Clone(),
 		parseWorkHandles:     s.parseWorkHandles.Clone(),
 		modTidyHandles:       s.modTidyHandles.Clone(),
@@ -1993,16 +1994,11 @@
 	// incref/decref operation that might destroy it prematurely.)
 	release := result.Acquire()
 
-	// Copy the set of unloadable files.
-	//
-	// TODO(rfindley): this looks wrong. Shouldn't we clear unloadableFiles on
+	// TODO(rfindley): this looks wrong. Should we clear unloadableFiles on
 	// changes to environment or workspace layout, or more generally on any
 	// metadata change?
 	//
 	// Maybe not, as major configuration changes cause a new view.
-	for k, v := range s.unloadableFiles {
-		result.unloadableFiles[k] = v
-	}
 
 	// Add all of the known subdirectories, but don't update them for the
 	// changed files. We need to rebuild the workspace module to know the
@@ -2119,7 +2115,7 @@
 		// TODO(rfindley): this also looks wrong, as typing in an unloadable file
 		// will result in repeated reloads. We should only delete if metadata
 		// changed.
-		delete(result.unloadableFiles, uri)
+		result.unloadableFiles.Remove(uri)
 	}
 
 	// Deleting an import can cause list errors due to import cycles to be