diff --git a/parser-typechecker/src/Unison/Codebase.hs b/parser-typechecker/src/Unison/Codebase.hs index a741477b0c..7d3fb7b8a1 100644 --- a/parser-typechecker/src/Unison/Codebase.hs +++ b/parser-typechecker/src/Unison/Codebase.hs @@ -150,7 +150,7 @@ import Unison.Parser.Ann (Ann) import Unison.Parser.Ann qualified as Parser import Unison.Prelude import Unison.Project (ProjectAndBranch (ProjectAndBranch), ProjectBranchName, ProjectName) -import Unison.Reference (Reference, TermReferenceId, TypeReference) +import Unison.Reference (Reference, TermReference, TermReferenceId, TypeReference) import Unison.Reference qualified as Reference import Unison.Referent qualified as Referent import Unison.Runtime.IOSource qualified as IOSource @@ -163,6 +163,7 @@ import Unison.Type qualified as Type import Unison.Typechecker.TypeLookup (TypeLookup (TypeLookup)) import Unison.Typechecker.TypeLookup qualified as TL import Unison.UnisonFile qualified as UF +import Unison.Util.Defns (Defns (..), DefnsF) import Unison.Util.Relation qualified as Rel import Unison.Var (Var) import Unison.WatchKind qualified as WK @@ -364,35 +365,51 @@ lookupWatchCache codebase h = do -- and all of their type dependencies, including builtins. typeLookupForDependencies :: Codebase IO Symbol Ann -> - Set Reference -> + DefnsF Set TermReference TypeReference -> Sqlite.Transaction (TL.TypeLookup Symbol Ann) typeLookupForDependencies codebase s = do when debug $ traceM $ "typeLookupForDependencies " ++ show s - (<> Builtin.typeLookup) <$> depthFirstAccum mempty s + (<> Builtin.typeLookup) <$> depthFirstAccum s where - depthFirstAccum :: TL.TypeLookup Symbol Ann -> Set Reference -> Sqlite.Transaction (TL.TypeLookup Symbol Ann) - depthFirstAccum tl refs = foldM go tl (Set.filter (unseen tl) refs) + depthFirstAccum :: + DefnsF Set TermReference TypeReference -> + Sqlite.Transaction (TL.TypeLookup Symbol Ann) + depthFirstAccum refs = do + tl <- depthFirstAccumTypes mempty refs.types + foldM goTerm tl (Set.filter (unseen tl) refs.terms) + + depthFirstAccumTypes :: + TL.TypeLookup Symbol Ann -> + Set TypeReference -> + Sqlite.Transaction (TL.TypeLookup Symbol Ann) + depthFirstAccumTypes tl refs = + foldM goType tl (Set.filter (unseen tl) refs) -- We need the transitive dependencies of data decls -- that are scrutinized in a match expression for -- pattern match coverage checking (specifically for -- the inhabitation check). We ensure these are found -- by collecting all transitive type dependencies. - go tl ref@(Reference.DerivedId id) = + goTerm :: TypeLookup Symbol Ann -> TermReference -> Sqlite.Transaction (TypeLookup Symbol Ann) + goTerm tl ref = getTypeOfTerm codebase ref >>= \case Just typ -> let z = tl <> TypeLookup (Map.singleton ref typ) mempty mempty - in depthFirstAccum z (Type.dependencies typ) - Nothing -> - getTypeDeclaration codebase id >>= \case - Just (Left ed) -> - let z = tl <> TypeLookup mempty mempty (Map.singleton ref ed) - in depthFirstAccum z (DD.typeDependencies $ DD.toDataDecl ed) - Just (Right dd) -> - let z = tl <> TypeLookup mempty (Map.singleton ref dd) mempty - in depthFirstAccum z (DD.typeDependencies dd) - Nothing -> pure tl - go tl Reference.Builtin {} = pure tl -- codebase isn't consulted for builtins + in depthFirstAccumTypes z (Type.dependencies typ) + Nothing -> pure tl + + goType :: TypeLookup Symbol Ann -> TypeReference -> Sqlite.Transaction (TypeLookup Symbol Ann) + goType tl ref@(Reference.DerivedId id) = + getTypeDeclaration codebase id >>= \case + Just (Left ed) -> + let z = tl <> TypeLookup mempty mempty (Map.singleton ref ed) + in depthFirstAccumTypes z (DD.typeDependencies $ DD.toDataDecl ed) + Just (Right dd) -> + let z = tl <> TypeLookup mempty (Map.singleton ref dd) mempty + in depthFirstAccumTypes z (DD.typeDependencies dd) + Nothing -> pure tl + goType tl Reference.Builtin {} = pure tl -- codebase isn't consulted for builtins + unseen :: TL.TypeLookup Symbol a -> Reference -> Bool unseen tl r = isNothing diff --git a/parser-typechecker/src/Unison/Codebase/CodeLookup.hs b/parser-typechecker/src/Unison/Codebase/CodeLookup.hs index bca52cecfb..aad2794519 100644 --- a/parser-typechecker/src/Unison/Codebase/CodeLookup.hs +++ b/parser-typechecker/src/Unison/Codebase/CodeLookup.hs @@ -8,6 +8,7 @@ import Unison.Prelude import Unison.Reference qualified as Reference import Unison.Term (Term) import Unison.Term qualified as Term +import Unison.Util.Defns (Defns (..)) import Unison.Util.Set qualified as Set import Unison.Var (Var) @@ -56,7 +57,7 @@ transitiveDependencies code seen0 rid = getIds = Set.mapMaybe Reference.toId in getTerm code rid >>= \case Just t -> - foldM (transitiveDependencies code) seen (getIds $ Term.dependencies t) + foldM (transitiveDependencies code) seen (getIds $ let deps = Term.dependencies t in deps.terms <> deps.types) Nothing -> getTypeDeclaration code rid >>= \case Nothing -> pure seen diff --git a/parser-typechecker/src/Unison/FileParsers.hs b/parser-typechecker/src/Unison/FileParsers.hs index cc02c9f736..d0673074e0 100644 --- a/parser-typechecker/src/Unison/FileParsers.hs +++ b/parser-typechecker/src/Unison/FileParsers.hs @@ -9,6 +9,7 @@ import Control.Lens import Control.Monad.State (evalStateT) import Data.Foldable qualified as Foldable import Data.List (partition) +import Data.List qualified as List import Data.List.NonEmpty qualified as List.NonEmpty import Data.Map qualified as Map import Data.Sequence qualified as Seq @@ -16,13 +17,14 @@ import Data.Set qualified as Set import Unison.ABT qualified as ABT import Unison.Blank qualified as Blank import Unison.Builtin qualified as Builtin +import Unison.ConstructorReference qualified as ConstructorReference import Unison.Name qualified as Name import Unison.Names qualified as Names import Unison.NamesWithHistory qualified as Names import Unison.Parser.Ann (Ann) import Unison.Prelude import Unison.PrettyPrintEnv.Names qualified as PPE -import Unison.Reference (Reference) +import Unison.Reference (TermReference, TypeReference) import Unison.Referent qualified as Referent import Unison.Result (CompilerBug (..), Note (..), ResultT, pattern Result) import Unison.Result qualified as Result @@ -37,6 +39,7 @@ import Unison.Typechecker.TypeLookup qualified as TL import Unison.UnisonFile (definitionLocation) import Unison.UnisonFile qualified as UF import Unison.UnisonFile.Names qualified as UF +import Unison.Util.Defns (Defns (..), DefnsF) import Unison.Util.List qualified as List import Unison.Util.Relation qualified as Rel import Unison.Var (Var) @@ -76,7 +79,7 @@ computeTypecheckingEnvironment :: (Var v, Monad m) => ShouldUseTndr m -> [Type v] -> - (Set Reference -> m (TL.TypeLookup v Ann)) -> + (DefnsF Set TermReference TypeReference -> m (TL.TypeLookup v Ann)) -> UnisonFile v -> m (Typechecker.Env v Ann) computeTypecheckingEnvironment shouldUseTndr ambientAbilities typeLookupf uf = @@ -99,8 +102,15 @@ computeTypecheckingEnvironment shouldUseTndr ambientAbilities typeLookupf uf = let shortname = Name.unsafeParseVar v, name `Name.endsWithReverseSegments` List.NonEmpty.toList (Name.reverseSegments shortname) ] - possibleRefs = Referent.toReference . view _3 <$> possibleDeps - tl <- fmap (UF.declsToTypeLookup uf <>) (typeLookupf (UF.dependencies uf <> Set.fromList possibleRefs)) + possibleRefs = + List.foldl' + ( \acc -> \case + (_, _, Referent.Con ref _) -> acc & over #types (Set.insert (ref ^. ConstructorReference.reference_)) + (_, _, Referent.Ref ref) -> acc & over #terms (Set.insert ref) + ) + (Defns Set.empty Set.empty) + possibleDeps + tl <- fmap (UF.declsToTypeLookup uf <>) (typeLookupf (UF.dependencies uf <> possibleRefs)) -- For populating the TDNR environment, we pick definitions -- from the namespace and from the local file whose full name -- has a suffix that equals one of the free variables in the file. @@ -130,7 +140,7 @@ computeTypecheckingEnvironment shouldUseTndr ambientAbilities typeLookupf uf = ] pure Typechecker.Env - { ambientAbilities = ambientAbilities, + { ambientAbilities, typeLookup = tl, termsByShortname = fqnsByShortName } diff --git a/parser-typechecker/src/Unison/UnisonFile.hs b/parser-typechecker/src/Unison/UnisonFile.hs index 7aaa1f5cd2..785482bac6 100644 --- a/parser-typechecker/src/Unison/UnisonFile.hs +++ b/parser-typechecker/src/Unison/UnisonFile.hs @@ -57,7 +57,7 @@ import Unison.Hashing.V2.Convert qualified as Hashing import Unison.LabeledDependency (LabeledDependency) import Unison.LabeledDependency qualified as LD import Unison.Prelude -import Unison.Reference (Reference) +import Unison.Reference (Reference, TermReference, TypeReference) import Unison.Reference qualified as Reference import Unison.Referent qualified as Referent import Unison.Term (Term) @@ -66,6 +66,7 @@ import Unison.Type (Type) import Unison.Type qualified as Type import Unison.Typechecker.TypeLookup qualified as TL import Unison.UnisonFile.Type (TypecheckedUnisonFile (..), UnisonFile (..), pattern TypecheckedUnisonFile, pattern UnisonFile) +import Unison.Util.Defns (Defns (..), DefnsF) import Unison.Util.List qualified as List import Unison.Var (Var) import Unison.Var qualified as Var @@ -84,7 +85,7 @@ emptyUnisonFile = leftBiasedMerge :: forall v a. (Ord v) => UnisonFile v a -> UnisonFile v a -> UnisonFile v a leftBiasedMerge lhs rhs = - let mergedTerms = Map.foldlWithKey' (addNotIn lhsTermNames) (terms lhs) (terms rhs) + let mergedTerms = Map.foldlWithKey' (addNotIn lhsTermNames) lhs.terms rhs.terms mergedWatches = Map.foldlWithKey' addWatch (watches lhs) (watches rhs) mergedDataDecls = Map.foldlWithKey' (addNotIn lhsTypeNames) (dataDeclarationsId lhs) (dataDeclarationsId rhs) mergedEffectDecls = Map.foldlWithKey' (addNotIn lhsTypeNames) (effectDeclarationsId lhs) (effectDeclarationsId rhs) @@ -96,7 +97,7 @@ leftBiasedMerge lhs rhs = } where lhsTermNames = - Map.keysSet (terms lhs) + Map.keysSet lhs.terms <> foldMap (\x -> Set.fromList [v | (v, _, _) <- x]) (watches lhs) lhsTypeNames = @@ -132,7 +133,7 @@ allWatches = join . Map.elems . watches -- | Get the location of a given definition in the file. definitionLocation :: (Var v) => v -> UnisonFile v a -> Maybe a definitionLocation v uf = - terms uf ^? ix v . _1 + uf.terms ^? ix v . _1 <|> watches uf ^? folded . folded . filteredBy (_1 . only v) . _2 <|> dataDeclarations uf ^? ix v . _2 . to DD.annotation <|> effectDeclarations uf ^? ix v . _2 . to (DD.annotation . DD.toDataDecl) @@ -152,7 +153,7 @@ typecheckingTerm uf = termBindings :: UnisonFile v a -> [(v, a, Term v a)] termBindings uf = - Map.foldrWithKey (\k (a, t) b -> (k, a, t) : b) [] (terms uf) + Map.foldrWithKey (\k (a, t) b -> (k, a, t) : b) [] uf.terms -- backwards compatibility with the old data type dataDeclarations' :: TypecheckedUnisonFile v a -> Map v (Reference, DataDeclaration v a) @@ -337,12 +338,20 @@ termSignatureExternalLabeledDependencies -- Returns the dependencies of the `UnisonFile` input. Needed so we can -- load information about these dependencies before starting typechecking. -dependencies :: (Monoid a, Var v) => UnisonFile v a -> Set Reference -dependencies (UnisonFile ds es ts ws) = - foldMap (DD.typeDependencies . snd) ds - <> foldMap (DD.typeDependencies . DD.toDataDecl . snd) es - <> foldMap (Term.dependencies . snd) ts - <> foldMap (foldMap (Term.dependencies . view _3)) ws +dependencies :: (Monoid a, Var v) => UnisonFile v a -> DefnsF Set TermReference TypeReference +dependencies file = + fold + [ Defns + { terms = Set.empty, + types = + Set.unions + [ foldMap (DD.typeDependencies . snd) file.dataDeclarationsId, + foldMap (DD.typeDependencies . DD.toDataDecl . snd) file.effectDeclarationsId + ] + }, + foldMap (Term.dependencies . snd) file.terms, + foldMap (foldMap (Term.dependencies . view _3)) file.watches + ] discardTypes :: (Ord v) => TypecheckedUnisonFile v a -> UnisonFile v a discardTypes (TypecheckedUnisonFileId datas effects terms watches _) = @@ -397,7 +406,7 @@ constructorsForDecls types uf = -- | All bindings in the term namespace: terms, test watches (since those are the only watches that are actually stored -- in the codebase), data constructors, and effect constructors. -termNamespaceBindings :: Ord v => TypecheckedUnisonFile v a -> Set v +termNamespaceBindings :: (Ord v) => TypecheckedUnisonFile v a -> Set v termNamespaceBindings uf = terms <> tests <> datacons <> effcons where @@ -413,7 +422,7 @@ termNamespaceBindings uf = uf.effectDeclarationsId' -- | All bindings in the term namespace: data declarations and effect declarations. -typeNamespaceBindings :: Ord v => TypecheckedUnisonFile v a -> Set v +typeNamespaceBindings :: (Ord v) => TypecheckedUnisonFile v a -> Set v typeNamespaceBindings uf = datas <> effs where diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs index c3fb06f800..d8166ae03a 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Merge2.hs @@ -280,8 +280,6 @@ doMerge info = do mergedLibdeps <- Cli.runTransaction (libdepsToBranch0 (Codebase.getDeclType env.codebase) blob2.libdeps) - uniqueName <- liftIO env.generateUniqueName - let hasConflicts = blob2.hasConflicts @@ -307,7 +305,7 @@ doMerge info = do maybeBlob5 <- if hasConflicts then pure Nothing - else case Merge.makeMergeblob4 blob3 uniqueName of + else case Merge.makeMergeblob4 blob3 of Left _parseErr -> pure Nothing Right blob4 -> do typeLookup <- Cli.runTransaction (Codebase.typeLookupForDependencies env.codebase blob4.dependencies) diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Run.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Run.hs index dcb684b168..05b68eedca 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/Run.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/Run.hs @@ -40,6 +40,7 @@ import Unison.Typechecker.TypeLookup qualified as TypeLookup import Unison.UnisonFile (TypecheckedUnisonFile) import Unison.UnisonFile qualified as UF import Unison.UnisonFile.Names qualified as UF +import Unison.Util.Defns (Defns (..)) import Unison.Var qualified as Var handleRun :: Bool -> HQ.HashQualified Name -> [String] -> Cli () @@ -124,7 +125,9 @@ getTerm' mainName = Cli.Env {codebase, runtime} <- ask case Typechecker.fitsScheme ty (Runtime.mainType runtime) of True -> do - typeLookup <- Cli.runTransaction (Codebase.typeLookupForDependencies codebase (Type.dependencies ty)) + typeLookup <- + Cli.runTransaction $ + Codebase.typeLookupForDependencies codebase Defns {terms = Set.empty, types = Type.dependencies ty} f $! synthesizeForce typeLookup ty False -> pure (TermHasBadType ty) in Cli.getLatestTypecheckedFile >>= \case diff --git a/unison-cli/src/Unison/Codebase/Editor/SlurpComponent.hs b/unison-cli/src/Unison/Codebase/Editor/SlurpComponent.hs index 82cc4a862a..3e51fb9aa2 100644 --- a/unison-cli/src/Unison/Codebase/Editor/SlurpComponent.hs +++ b/unison-cli/src/Unison/Codebase/Editor/SlurpComponent.hs @@ -25,21 +25,22 @@ import Data.Set qualified as Set import Data.Tuple (swap) import Unison.DataDeclaration qualified as DD import Unison.Prelude hiding (empty) -import Unison.Reference (Reference) +import Unison.Reference (TypeReference) import Unison.Symbol (Symbol) import Unison.Term qualified as Term import Unison.UnisonFile (TypecheckedUnisonFile) import Unison.UnisonFile qualified as UF +import Unison.Util.Defns (Defns (..)) data SlurpComponent = SlurpComponent { types :: Set Symbol, terms :: Set Symbol, ctors :: Set Symbol } - deriving (Eq, Ord, Show) + deriving (Eq, Generic, Ord, Show) isEmpty :: SlurpComponent -> Bool -isEmpty sc = Set.null (types sc) && Set.null (terms sc) && Set.null (ctors sc) +isEmpty sc = Set.null sc.types && Set.null sc.terms && Set.null sc.ctors empty :: SlurpComponent empty = SlurpComponent {types = Set.empty, terms = Set.empty, ctors = Set.empty} @@ -47,23 +48,23 @@ empty = SlurpComponent {types = Set.empty, terms = Set.empty, ctors = Set.empty} difference :: SlurpComponent -> SlurpComponent -> SlurpComponent difference c1 c2 = SlurpComponent {types = types', terms = terms', ctors = ctors'} where - types' = types c1 `Set.difference` types c2 - terms' = terms c1 `Set.difference` terms c2 - ctors' = ctors c1 `Set.difference` ctors c2 + types' = c1.types `Set.difference` c2.types + terms' = c1.terms `Set.difference` c2.terms + ctors' = c1.ctors `Set.difference` c2.ctors intersection :: SlurpComponent -> SlurpComponent -> SlurpComponent intersection c1 c2 = SlurpComponent {types = types', terms = terms', ctors = ctors'} where - types' = types c1 `Set.intersection` types c2 - terms' = terms c1 `Set.intersection` terms c2 - ctors' = ctors c1 `Set.intersection` ctors c2 + types' = c1.types `Set.intersection` c2.types + terms' = c1.terms `Set.intersection` c2.terms + ctors' = c1.ctors `Set.intersection` c2.ctors instance Semigroup SlurpComponent where c1 <> c2 = SlurpComponent - { types = types c1 <> types c2, - terms = terms c1 <> terms c2, - ctors = ctors c1 <> ctors c2 + { types = c1.types <> c2.types, + terms = c1.terms <> c2.terms, + ctors = c1.ctors <> c2.ctors } instance Monoid SlurpComponent where @@ -79,31 +80,30 @@ closeWithDependencies :: SlurpComponent closeWithDependencies uf inputs = seenDefns {ctors = constructorDeps} where - seenDefns = foldl' termDeps (SlurpComponent {terms = mempty, types = seenTypes, ctors = mempty}) (terms inputs) - seenTypes = foldl' typeDeps mempty (types inputs) + seenDefns = foldl' termDeps (SlurpComponent {terms = mempty, types = seenTypes, ctors = mempty}) inputs.terms + seenTypes = foldl' typeDeps mempty inputs.types constructorDeps :: Set Symbol constructorDeps = UF.constructorsForDecls seenTypes uf termDeps :: SlurpComponent -> Symbol -> SlurpComponent - termDeps seen v | Set.member v (terms seen) = seen - termDeps seen v = fromMaybe seen $ do + termDeps seen v | Set.member v seen.terms = seen + termDeps seen v = fromMaybe seen do term <- findTerm v let -- get the `v`s for the transitive dependency types -- (the ones for terms are just the `freeVars below`) -- although this isn't how you'd do it for a term that's already in codebase tdeps :: [Symbol] - tdeps = resolveTypes $ Term.dependencies term + tdeps = resolveTypes (Term.dependencies term).types seenTypes :: Set Symbol - seenTypes = foldl' typeDeps (types seen) tdeps - seenTerms = Set.insert v (terms seen) + seenTypes = foldl' typeDeps seen.types tdeps + seenTerms = Set.insert v seen.terms pure $ foldl' termDeps ( seen - { types = seenTypes, - terms = seenTerms - } + & #types .~ seenTypes + & #terms .~ seenTerms ) (Term.freeVars term) @@ -115,7 +115,7 @@ closeWithDependencies uf inputs = seenDefns {ctors = constructorDeps} <|> fmap (DD.toDataDecl . snd) (Map.lookup v (UF.effectDeclarations' uf)) pure $ foldl' typeDeps (Set.insert v seen) (resolveTypes $ DD.typeDependencies dd) - resolveTypes :: Set Reference -> [Symbol] + resolveTypes :: Set TypeReference -> [Symbol] resolveTypes rs = [v | r <- Set.toList rs, Just v <- [Map.lookup r typeNames]] findTerm :: Symbol -> Maybe (Term.Term Symbol a) @@ -123,17 +123,17 @@ closeWithDependencies uf inputs = seenDefns {ctors = constructorDeps} allTerms = UF.allTerms uf - typeNames :: Map Reference Symbol + typeNames :: Map TypeReference Symbol typeNames = invert (fst <$> UF.dataDeclarations' uf) <> invert (fst <$> UF.effectDeclarations' uf) invert :: forall k v. (Ord k) => (Ord v) => Map k v -> Map v k invert m = Map.fromList (swap <$> Map.toList m) fromTypes :: Set Symbol -> SlurpComponent -fromTypes vs = mempty {types = vs} +fromTypes vs = SlurpComponent {terms = Set.empty, types = vs, ctors = Set.empty} fromTerms :: Set Symbol -> SlurpComponent -fromTerms vs = mempty {terms = vs} +fromTerms vs = SlurpComponent {terms = vs, types = Set.empty, ctors = Set.empty} fromCtors :: Set Symbol -> SlurpComponent -fromCtors vs = mempty {ctors = vs} +fromCtors vs = SlurpComponent {terms = Set.empty, types = Set.empty, ctors = vs} diff --git a/unison-core/src/Unison/DataDeclaration.hs b/unison-core/src/Unison/DataDeclaration.hs index 513759ac07..0421751c8e 100644 --- a/unison-core/src/Unison/DataDeclaration.hs +++ b/unison-core/src/Unison/DataDeclaration.hs @@ -49,7 +49,7 @@ import Unison.LabeledDependency qualified as LD import Unison.Name qualified as Name import Unison.Names.ResolutionResult qualified as Names import Unison.Prelude -import Unison.Reference (Reference) +import Unison.Reference (Reference, TypeReference) import Unison.Reference qualified as Reference import Unison.Referent qualified as Referent import Unison.ReferentPrime qualified as Referent' @@ -222,7 +222,7 @@ bindReferences unsafeVarToName keepFree names (DataDeclaration m a bound constru -- (unless the decl is self-referential) -- Note: Does NOT include the referents for fields and field accessors. -- Those must be computed separately because we need access to the typechecker to do so. -typeDependencies :: (Ord v) => DataDeclaration v a -> Set Reference +typeDependencies :: (Ord v) => DataDeclaration v a -> Set TypeReference typeDependencies dd = Set.unions (Type.dependencies <$> constructorTypes dd) diff --git a/unison-core/src/Unison/Term.hs b/unison-core/src/Unison/Term.hs index 5a3ea2127a..92cb5ccf31 100644 --- a/unison-core/src/Unison/Term.hs +++ b/unison-core/src/Unison/Term.hs @@ -8,6 +8,7 @@ import Control.Monad.State (evalState) import Control.Monad.State qualified as State import Control.Monad.Writer.Strict qualified as Writer import Data.Generics.Sum (_Ctor) +import Data.List qualified as List import Data.Map qualified as Map import Data.Sequence qualified as Sequence import Data.Set qualified as Set @@ -17,6 +18,7 @@ import Text.Show import Unison.ABT qualified as ABT import Unison.Blank qualified as B import Unison.ConstructorReference (ConstructorReference, GConstructorReference (..)) +import Unison.ConstructorReference qualified as ConstructorReference import Unison.ConstructorType qualified as CT import Unison.DataDeclaration.ConstructorId (ConstructorId) import Unison.HashQualified qualified as HQ @@ -30,12 +32,13 @@ import Unison.NamesWithHistory qualified as Names import Unison.Pattern (Pattern) import Unison.Pattern qualified as Pattern import Unison.Prelude -import Unison.Reference (Reference, TermReference, pattern Builtin) +import Unison.Reference (Reference, TermReference, TypeReference, pattern Builtin) import Unison.Reference qualified as Reference import Unison.Referent (Referent) import Unison.Referent qualified as Referent import Unison.Type (Type) import Unison.Type qualified as Type +import Unison.Util.Defns (Defns (..), DefnsF) import Unison.Util.List (multimap, validate) import Unison.Var (Var) import Unison.Var qualified as Var @@ -1211,27 +1214,27 @@ unReqOrCtor (Request' r) = Just r unReqOrCtor _ = Nothing -- Dependencies including referenced data and effect decls -dependencies :: (Ord v, Ord vt) => Term2 vt at ap v a -> Set Reference -dependencies t = Set.map (LD.fold id Referent.toReference) (labeledDependencies t) +dependencies :: (Ord v, Ord vt) => Term2 vt at ap v a -> DefnsF Set TermReference TypeReference +dependencies = + List.foldl' f (Defns Set.empty Set.empty) . Set.toList . labeledDependencies + where + f :: + DefnsF Set TermReference TypeReference -> + LabeledDependency -> + DefnsF Set TermReference TypeReference + f deps = \case + LD.TermReferent (Referent.Con ref _) -> deps & over #types (Set.insert (ref ^. ConstructorReference.reference_)) + LD.TermReferent (Referent.Ref ref) -> deps & over #terms (Set.insert ref) + LD.TypeReference ref -> deps & over #types (Set.insert ref) termDependencies :: (Ord v, Ord vt) => Term2 vt at ap v a -> Set TermReference termDependencies = - Set.fromList - . mapMaybe - ( LD.fold - (\_typeRef -> Nothing) - ( Referent.fold - (\termRef -> Just termRef) - (\_typeConRef _i _ct -> Nothing) - ) - ) - . toList - . labeledDependencies + (.terms) . dependencies -- gets types from annotations and constructors typeDependencies :: (Ord v, Ord vt) => Term2 vt at ap v a -> Set Reference typeDependencies = - Set.fromList . mapMaybe (LD.fold Just (const Nothing)) . toList . labeledDependencies + (.types) . dependencies -- Gets the types to which this term contains references via patterns and -- data constructors. diff --git a/unison-merge/src/Unison/Merge/Mergeblob2.hs b/unison-merge/src/Unison/Merge/Mergeblob2.hs index 4f3491efe8..fc76660bbe 100644 --- a/unison-merge/src/Unison/Merge/Mergeblob2.hs +++ b/unison-merge/src/Unison/Merge/Mergeblob2.hs @@ -14,7 +14,6 @@ import Unison.DeclNameLookup (DeclNameLookup) import Unison.Merge.EitherWay (EitherWay (..)) import Unison.Merge.FindConflictedAlias (findConflictedAlias) import Unison.Merge.Mergeblob1 (Mergeblob1 (..)) -import Unison.Merge.PartialDeclNameLookup (PartialDeclNameLookup) import Unison.Merge.PartitionCombinedDiffs (narrowConflictsToNonBuiltins) import Unison.Merge.ThreeWay (ThreeWay) import Unison.Merge.ThreeWay qualified as ThreeWay @@ -40,8 +39,6 @@ import Unison.Util.Defns (Defns (..), DefnsF, defnsAreEmpty, zipDefnsWith) data Mergeblob2 libdep = Mergeblob2 { conflicts :: TwoWay (DefnsF (Map Name) TermReferenceId TypeReferenceId), - conflictsIds :: TwoWay (DefnsF Set TermReferenceId TypeReferenceId), - conflictsNames :: TwoWay (DefnsF Set Name Name), coreDependencies :: TwoWay (DefnsF Set TermReference TypeReference), declNameLookups :: TwoWay DeclNameLookup, defns :: ThreeWay (Defns (BiMultimap Referent Name) (BiMultimap TypeReference Name)), @@ -53,7 +50,6 @@ data Mergeblob2 libdep = Mergeblob2 (TermReferenceId, (Term Symbol Ann, Type Symbol Ann)) (TypeReferenceId, Decl Symbol Ann) ), - lcaDeclNameLookup :: PartialDeclNameLookup, libdeps :: Map NameSegment libdep, soloUpdatesAndDeletes :: TwoWay (DefnsF Set Name Name), unconflicts :: DefnsF Unconflicts Referent TypeReference @@ -71,24 +67,27 @@ makeMergeblob2 blob = do Left . Mergeblob2Error'ConflictedAlias . who conflicts <- narrowConflictsToNonBuiltins blob.conflicts & mapLeft Mergeblob2Error'ConflictedBuiltin - let conflictsIds = bimap (Set.fromList . Map.elems) (Set.fromList . Map.elems) <$> conflicts - let conflictsNames = bimap Map.keysSet Map.keysSet <$> conflicts - let soloUpdatesAndDeletes = Unconflicts.soloUpdatesAndDeletes blob.unconflicts - let coreDependencies = identifyCoreDependencies (ThreeWay.forgetLca blob.defns) conflictsIds soloUpdatesAndDeletes + let soloUpdatesAndDeletes :: TwoWay (DefnsF Set Name Name) + soloUpdatesAndDeletes = + Unconflicts.soloUpdatesAndDeletes blob.unconflicts + + let coreDependencies :: TwoWay (DefnsF Set TermReference TypeReference) + coreDependencies = + identifyCoreDependencies + (ThreeWay.forgetLca blob.defns) + (bimap (Set.fromList . Map.elems) (Set.fromList . Map.elems) <$> conflicts) + soloUpdatesAndDeletes pure Mergeblob2 { conflicts, - conflictsIds, - conflictsNames, coreDependencies, declNameLookups = blob.declNameLookups, defns = blob.defns, -- Eh, they'd either both be null, or neither, but just check both maps anyway hasConflicts = not (defnsAreEmpty conflicts.alice) || not (defnsAreEmpty conflicts.bob), hydratedDefns = ThreeWay.forgetLca blob.hydratedDefns, - lcaDeclNameLookup = blob.lcaDeclNameLookup, libdeps = blob.libdeps, soloUpdatesAndDeletes, unconflicts = blob.unconflicts diff --git a/unison-merge/src/Unison/Merge/Mergeblob3.hs b/unison-merge/src/Unison/Merge/Mergeblob3.hs index 6133c404d0..97e11a8c08 100644 --- a/unison-merge/src/Unison/Merge/Mergeblob3.hs +++ b/unison-merge/src/Unison/Merge/Mergeblob3.hs @@ -15,6 +15,7 @@ import Data.Set.NonEmpty qualified as Set.NonEmpty import Data.These (These (..)) import Data.Zip (unzip) import Unison.DataDeclaration (Decl) +import Unison.DataDeclaration qualified as DataDeclaration import Unison.DeclNameLookup (DeclNameLookup, expectConstructorNames) import Unison.Merge.Mergeblob2 (Mergeblob2 (..)) import Unison.Merge.PrettyPrintEnv (makePrettyPrintEnvs) @@ -31,6 +32,7 @@ import Unison.Reference (Reference' (..), TermReferenceId, TypeReference, TypeRe import Unison.Referent (Referent) import Unison.Referent qualified as Referent import Unison.Symbol (Symbol) +import Unison.Syntax.FilePrinter (renderDefnsForUnisonFile) import Unison.Syntax.Name qualified as Name import Unison.Term (Term) import Unison.Type (Type) @@ -41,11 +43,11 @@ import Unison.Util.Pretty (ColorText, Pretty) import Unison.Util.Pretty qualified as Pretty import Unison.Util.Relation qualified as Relation import Prelude hiding (unzip) -import Unison.Syntax.FilePrinter (renderDefnsForUnisonFile) data Mergeblob3 = Mergeblob3 { libdeps :: Names, stageOne :: DefnsF (Map Name) Referent TypeReference, + uniqueTypeGuids :: Map Name Text, unparsedFile :: Pretty ColorText } @@ -56,11 +58,15 @@ makeMergeblob3 :: TwoWay Text -> Mergeblob3 makeMergeblob3 blob dependents0 libdeps authors = - -- Identify the unconflicted dependents we need to pull into the Unison file (either first for typechecking, if - -- there aren't conflicts, or else for manual conflict resolution without a typechecking step, if there are) - let dependents = + let conflictsNames :: TwoWay (DefnsF Set Name Name) + conflictsNames = + bimap Map.keysSet Map.keysSet <$> blob.conflicts + + -- Identify the unconflicted dependents we need to pull into the Unison file (either first for typechecking, if + -- there aren't conflicts, or else for manual conflict resolution without a typechecking step, if there are) + dependents = filterDependents - blob.conflictsNames + conflictsNames blob.soloUpdatesAndDeletes ( let f :: Set TermReferenceId -> Referent -> NESet Name -> Set Name f deps defn0 names @@ -85,7 +91,7 @@ makeMergeblob3 blob dependents0 libdeps authors = renderConflictsAndDependents blob.declNameLookups blob.hydratedDefns - blob.conflictsNames + conflictsNames dependents (defnsToNames <$> ThreeWay.forgetLca blob.defns) libdeps @@ -94,10 +100,11 @@ makeMergeblob3 blob dependents0 libdeps authors = stageOne = makeStageOne blob.declNameLookups - blob.conflictsNames + conflictsNames blob.unconflicts dependents (bimap BiMultimap.range BiMultimap.range blob.defns.lca), + uniqueTypeGuids = makeUniqueTypeGuids blob.hydratedDefns, unparsedFile = makePrettyUnisonFile authors renderedConflicts renderedDependents } @@ -204,7 +211,7 @@ renderConflictsAndDependents :: renderConflictsAndDependents declNameLookups hydratedDefns conflicts dependents names libdepsNames = unzip $ ( \declNameLookup (conflicts, dependents) ppe -> - let render = renderDefnsForUnisonFile declNameLookup ppe . over (#terms . mapped) snd + let render = renderDefnsForUnisonFile declNameLookup ppe . over (#terms . mapped) snd in (render conflicts, render dependents) ) <$> declNameLookups @@ -291,3 +298,46 @@ makePrettyUnisonFile authors conflicts dependents = bimap f f where f = map snd . List.sortOn (Name.toText . fst) . Map.toList + +-- Given Alice's and Bob's hydrated defns, make a mapping from unique type name to unique type GUID, preferring Alice's +-- GUID if they both have one. +makeUniqueTypeGuids :: + TwoWay + ( DefnsF + (Map Name) + (TermReferenceId, (Term Symbol Ann, Type Symbol Ann)) + (TypeReferenceId, Decl Symbol Ann) + ) -> + Map Name Text +makeUniqueTypeGuids hydratedDefns = + let -- Start off with just Alice's GUIDs + aliceGuids :: Map Name Text + aliceGuids = + Map.mapMaybe (declGuid . snd) hydratedDefns.alice.types + + -- Define a helper that adds a Bob GUID only if it's not already in the map (so, preferring Alice) + addBobGuid :: Map Name Text -> (Name, (TypeReferenceId, Decl Symbol Ann)) -> Map Name Text + addBobGuid acc (name, (_, bobDecl)) = + Map.alter + ( \case + Nothing -> bobGuid + Just aliceGuid -> Just aliceGuid + ) + name + acc + where + bobGuid :: Maybe Text + bobGuid = + declGuid bobDecl + + -- Tumble in all of Bob's GUIDs with that helper + allTheGuids :: Map Name Text + allTheGuids = + List.foldl' addBobGuid aliceGuids (Map.toList hydratedDefns.bob.types) + in allTheGuids + where + declGuid :: Decl v a -> Maybe Text + declGuid decl = + case (DataDeclaration.asDataDecl decl).modifier of + DataDeclaration.Structural -> Nothing + DataDeclaration.Unique guid -> Just guid diff --git a/unison-merge/src/Unison/Merge/Mergeblob4.hs b/unison-merge/src/Unison/Merge/Mergeblob4.hs index 6a3631111d..3a72e4c854 100644 --- a/unison-merge/src/Unison/Merge/Mergeblob4.hs +++ b/unison-merge/src/Unison/Merge/Mergeblob4.hs @@ -4,38 +4,39 @@ module Unison.Merge.Mergeblob4 ) where +import Data.Map.Strict qualified as Map import Unison.Merge.Mergeblob3 (Mergeblob3 (..)) import Unison.Names (Names (..)) import Unison.Parser.Ann (Ann) import Unison.Parsers qualified as Parsers import Unison.Prelude -import Unison.Reference (Reference) +import Unison.Reference (TermReference, TypeReference) import Unison.Symbol (Symbol) -import Unison.Syntax.Parser (ParsingEnv (..), UniqueName) +import Unison.Syntax.Parser (ParsingEnv (..)) import Unison.Syntax.Parser qualified as Parser import Unison.UnisonFile (UnisonFile) import Unison.UnisonFile qualified as UnisonFile -import Unison.Util.Defns (Defns (..)) +import Unison.Util.Defns (Defns (..), DefnsF) import Unison.Util.Pretty qualified as Pretty import Unison.Util.Relation qualified as Relation data Mergeblob4 = Mergeblob4 - { dependencies :: Set Reference, + { dependencies :: DefnsF Set TermReference TypeReference, file :: UnisonFile Symbol Ann } -makeMergeblob4 :: Mergeblob3 -> UniqueName -> Either (Parser.Err Symbol) Mergeblob4 -makeMergeblob4 blob uniqueName = do +makeMergeblob4 :: Mergeblob3 -> Either (Parser.Err Symbol) Mergeblob4 +makeMergeblob4 blob = do let stageOneNames = Names (Relation.fromMap blob.stageOne.terms) (Relation.fromMap blob.stageOne.types) <> blob.libdeps parsingEnv = ParsingEnv - { uniqueNames = uniqueName, - -- The codebase names are disjoint from the file names, i.e. there aren't any things that - -- would be classified as an update upon parsing. So, there's no need to try to look up any - -- existing unique type GUIDs to reuse. - uniqueTypeGuid = \_ -> Identity Nothing, + { -- We don't expect to have to generate any new GUIDs, since the uniqueTypeGuid lookup function below should + -- cover all name in the merged file we're about to parse and typecheck. So, this might be more correct as a + -- call to `error`. + uniqueNames = Parser.UniqueName \_ _ -> Nothing, + uniqueTypeGuid = \name -> Identity (Map.lookup name blob.uniqueTypeGuids), names = stageOneNames } file <- runIdentity (Parsers.parseFile "" (Pretty.toPlain 80 blob.unparsedFile) parsingEnv) diff --git a/unison-src/transcripts/merge.output.md b/unison-src/transcripts/merge.output.md index 6f4eba070d..9dea5fdcf6 100644 --- a/unison-src/transcripts/merge.output.md +++ b/unison-src/transcripts/merge.output.md @@ -1024,7 +1024,7 @@ Bob, meanwhile, first deletes the term, then sort of deletes the type and re-add ``` ucm project/bob> view Foo.Bar - type Foo.Bar = Hello Nat Nat | Baz Nat + type Foo.Bar = Baz Nat | Hello Nat Nat ``` At this point, Bob and alice have both updated the name `Foo.Bar.Hello` in different ways, so that's a conflict. Therefore, Bob's entire type (`Foo.Bar` with constructors `Foo.Bar.Baz` and `Foo.Bar.Hello`) gets rendered into the scratch file. @@ -1061,7 +1061,7 @@ Foo.Bar.Hello : Nat Foo.Bar.Hello = 18 -- project/bob -type Foo.Bar = Hello Nat Nat | Baz Nat +type Foo.Bar = Baz Nat | Hello Nat Nat ``` diff --git a/unison-syntax/src/Unison/Syntax/Parser.hs b/unison-syntax/src/Unison/Syntax/Parser.hs index 6c4aa74b95..1ac87e8eb2 100644 --- a/unison-syntax/src/Unison/Syntax/Parser.hs +++ b/unison-syntax/src/Unison/Syntax/Parser.hs @@ -9,7 +9,7 @@ module Unison.Syntax.Parser Input (..), P, ParsingEnv (..), - UniqueName, + UniqueName (..), anyToken, blank, bytesToken,