mirror of
https://github.com/unisonweb/unison.git
synced 2024-09-11 10:35:57 +03:00
Merge branch 'trunk' into cp/project-root
# Conflicts: # unison-cli-integration/integration-tests/IntegrationTests/transcript.output.md # unison-src/transcripts-round-trip/main.output.md # unison-src/transcripts/add-run.output.md # unison-src/transcripts/bug-strange-closure.output.md # unison-src/transcripts/cycle-update-5.output.md # unison-src/transcripts/delete.output.md # unison-src/transcripts/diff-namespace.output.md # unison-src/transcripts/move-namespace.output.md # unison-src/transcripts/name-selection.output.md # unison-src/transcripts/names.output.md # unison-src/transcripts/namespace-dependencies.output.md # unison-src/transcripts/propagate.output.md # unison-src/transcripts/reflog.output.md # unison-src/transcripts/reset.output.md # unison-src/transcripts/tab-completion.output.md # unison-src/transcripts/transcript-parser-commands.output.md
This commit is contained in:
commit
0cd3cd1cff
@ -178,6 +178,7 @@ invertDomain =
|
||||
g x acc y =
|
||||
Map.insert y x acc
|
||||
|
||||
-- | Construct a left-unique relation from a mapping from its right-elements to its left-elements.
|
||||
fromRange :: (Ord a, Ord b) => Map b a -> BiMultimap a b
|
||||
fromRange m =
|
||||
BiMultimap (Map.foldlWithKey' f Map.empty m) m
|
||||
|
@ -139,6 +139,7 @@ import Unison.NameSegment (NameSegment)
|
||||
import Unison.NameSegment qualified as NameSegment
|
||||
import Unison.Prelude hiding (empty)
|
||||
import Unison.Reference (TermReference, TermReferenceId, TypeReference, TypeReferenceId)
|
||||
import Unison.Reference qualified as Reference
|
||||
import Unison.Referent (Referent)
|
||||
import Unison.Referent qualified as Referent
|
||||
import Unison.Util.List qualified as List
|
||||
@ -148,7 +149,6 @@ import Unison.Util.Set qualified as Set
|
||||
import Unison.Util.Star2 qualified as Star2
|
||||
import Witherable (FilterableWithIndex (imapMaybe))
|
||||
import Prelude hiding (head, read, subtract)
|
||||
import qualified Unison.Reference as Reference
|
||||
|
||||
instance AsEmpty (Branch m) where
|
||||
_Empty = prism' (const empty) matchEmpty
|
||||
@ -215,7 +215,6 @@ deepTypeReferenceIds :: Branch0 m -> Set TypeReferenceId
|
||||
deepTypeReferenceIds =
|
||||
Set.mapMaybe Reference.toId . deepTypeReferences
|
||||
|
||||
|
||||
namespaceStats :: Branch0 m -> NamespaceStats
|
||||
namespaceStats b =
|
||||
NamespaceStats
|
||||
|
@ -1,7 +1,7 @@
|
||||
{-# LANGUAGE TemplateHaskell #-}
|
||||
|
||||
module Unison.Codebase.Causal
|
||||
( Causal (currentHash, head, tail, tails),
|
||||
( Causal (currentHash, valueHash, head, tail, tails),
|
||||
pattern One,
|
||||
pattern Cons,
|
||||
pattern Merge,
|
||||
@ -40,7 +40,8 @@ import Unison.Codebase.Causal.Type
|
||||
currentHash,
|
||||
head,
|
||||
tail,
|
||||
tails
|
||||
tails,
|
||||
valueHash
|
||||
),
|
||||
before,
|
||||
lca,
|
||||
|
@ -19,6 +19,7 @@ dependencies:
|
||||
- base
|
||||
- bytes
|
||||
- bytestring
|
||||
- cmark
|
||||
- co-log-core
|
||||
- code-page
|
||||
- concurrent-output
|
||||
|
@ -9,6 +9,9 @@ module Unison.Codebase.Editor.HandleInput.Merge2
|
||||
LcaMergeInfo (..),
|
||||
doMerge,
|
||||
doMergeLocalBranch,
|
||||
|
||||
-- * API exported for @todo@
|
||||
hasDefnsInLib,
|
||||
)
|
||||
where
|
||||
|
||||
@ -241,11 +244,7 @@ doMerge info = do
|
||||
|
||||
-- Assert that neither Alice nor Bob have defns in lib
|
||||
for_ [(mergeTarget, branches.alice), (mergeSource, branches.bob)] \(who, branch) -> do
|
||||
libdeps <-
|
||||
case Map.lookup NameSegment.libSegment branch.children of
|
||||
Nothing -> pure V2.Branch.empty
|
||||
Just libdeps -> Cli.runTransaction libdeps.value
|
||||
when (not (Map.null libdeps.terms) || not (Map.null libdeps.types)) do
|
||||
whenM (Cli.runTransaction (hasDefnsInLib branch)) do
|
||||
done (Output.MergeDefnsInLib who)
|
||||
|
||||
-- Load Alice/Bob/LCA definitions and decl name lookups
|
||||
@ -487,6 +486,17 @@ loadLibdeps branches = do
|
||||
libdepsBranch <- libdepsCausal.value
|
||||
pure libdepsBranch.children
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- Merge precondition violation checks
|
||||
|
||||
hasDefnsInLib :: Applicative m => V2.Branch m -> m Bool
|
||||
hasDefnsInLib branch = do
|
||||
libdeps <-
|
||||
case Map.lookup NameSegment.libSegment branch.children of
|
||||
Nothing -> pure V2.Branch.empty
|
||||
Just libdeps -> libdeps.value
|
||||
pure (not (Map.null libdeps.terms) || not (Map.null libdeps.types))
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- Creating Unison files
|
||||
|
||||
|
@ -4,7 +4,9 @@ module Unison.Codebase.Editor.HandleInput.Todo
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Either qualified as Either
|
||||
import Data.Set qualified as Set
|
||||
import U.Codebase.HashTags (BranchHash (..))
|
||||
import U.Codebase.Sqlite.Operations qualified as Operations
|
||||
import Unison.Builtin qualified as Builtin
|
||||
import Unison.Cli.Monad (Cli)
|
||||
@ -14,7 +16,11 @@ import Unison.Cli.PrettyPrintUtils qualified as Cli
|
||||
import Unison.Codebase qualified as Codebase
|
||||
import Unison.Codebase.Branch qualified as Branch
|
||||
import Unison.Codebase.Branch.Names qualified as Branch
|
||||
import Unison.Codebase.Causal qualified as Causal
|
||||
import Unison.Codebase.Editor.HandleInput.Merge2 (hasDefnsInLib)
|
||||
import Unison.Codebase.Editor.Output
|
||||
import Unison.Hash (HashFor (..))
|
||||
import Unison.Merge.DeclCoherencyCheck (IncoherentDeclReasons (..), checkAllDeclCoherency)
|
||||
import Unison.Names qualified as Names
|
||||
import Unison.Prelude
|
||||
import Unison.Reference (TermReference)
|
||||
@ -26,11 +32,22 @@ handleTodo :: Cli ()
|
||||
handleTodo = do
|
||||
-- For now, we don't go through any great trouble to seek out the root of the project branch. Just assume the current
|
||||
-- namespace is the root, which will be the case unless the user uses `deprecated.cd`.
|
||||
currentNamespace <- Cli.getCurrentBranch0
|
||||
currentCausal <- Cli.getCurrentBranch
|
||||
let currentNamespace = Branch.head currentCausal
|
||||
let currentNamespaceWithoutLibdeps = Branch.deleteLibdeps currentNamespace
|
||||
|
||||
(dependentsOfTodo, directDependencies, hashLen) <-
|
||||
(defnsInLib, dependentsOfTodo, directDependencies, hashLen, incoherentDeclReasons) <-
|
||||
Cli.runTransaction do
|
||||
-- We call a shared `hasDefnsLib` helper even though we could easily duplicate the logic with the branch in hand
|
||||
defnsInLib <- do
|
||||
branch <-
|
||||
currentCausal
|
||||
& Branch._history
|
||||
& Causal.valueHash
|
||||
& coerce @_ @BranchHash
|
||||
& Operations.expectBranchByBranchHash
|
||||
hasDefnsInLib branch
|
||||
|
||||
let todoReference :: TermReference
|
||||
todoReference =
|
||||
Set.asSingleton (Names.refTermsNamed Builtin.names (Name.unsafeParseText "todo"))
|
||||
@ -51,20 +68,28 @@ handleTodo = do
|
||||
|
||||
hashLen <- Codebase.hashLength
|
||||
|
||||
pure (dependentsOfTodo.terms, directDependencies, hashLen)
|
||||
incoherentDeclReasons <-
|
||||
fmap (Either.fromLeft (IncoherentDeclReasons [] [] [] [])) $
|
||||
checkAllDeclCoherency
|
||||
Operations.expectDeclNumConstructors
|
||||
(Names.lenientToNametree (Branch.toNames currentNamespaceWithoutLibdeps))
|
||||
|
||||
pure (defnsInLib, dependentsOfTodo.terms, directDependencies, hashLen, incoherentDeclReasons)
|
||||
|
||||
ppe <- Cli.currentPrettyPrintEnvDecl
|
||||
|
||||
Cli.respondNumbered $
|
||||
Output'Todo
|
||||
TodoOutput
|
||||
{ hashLen,
|
||||
{ defnsInLib,
|
||||
dependentsOfTodo,
|
||||
directDependenciesWithoutNames =
|
||||
Defns
|
||||
{ terms = Set.difference directDependencies.terms (Branch.deepTermReferences currentNamespace),
|
||||
types = Set.difference directDependencies.types (Branch.deepTypeReferences currentNamespace)
|
||||
},
|
||||
hashLen,
|
||||
incoherentDeclReasons,
|
||||
nameConflicts = Names.conflicts (Branch.toNames currentNamespaceWithoutLibdeps),
|
||||
ppe
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ import Unison.Hash (Hash)
|
||||
import Unison.HashQualified qualified as HQ
|
||||
import Unison.HashQualified' qualified as HQ'
|
||||
import Unison.LabeledDependency (LabeledDependency)
|
||||
import Unison.Merge.DeclCoherencyCheck (IncoherentDeclReasons (..))
|
||||
import Unison.Name (Name)
|
||||
import Unison.NameSegment (NameSegment)
|
||||
import Unison.Names (Names)
|
||||
@ -161,9 +162,11 @@ data NumberedOutput
|
||||
[ProjectReflog.Entry Project ProjectBranch (CausalHash, SCH.ShortCausalHash)]
|
||||
|
||||
data TodoOutput = TodoOutput
|
||||
{ dependentsOfTodo :: !(Set TermReferenceId),
|
||||
{ defnsInLib :: !Bool,
|
||||
dependentsOfTodo :: !(Set TermReferenceId),
|
||||
directDependenciesWithoutNames :: !(DefnsF Set TermReference TypeReference),
|
||||
hashLen :: !Int,
|
||||
incoherentDeclReasons :: !IncoherentDeclReasons,
|
||||
nameConflicts :: !Names,
|
||||
ppe :: !PrettyPrintEnvDecl
|
||||
}
|
||||
@ -173,6 +176,8 @@ todoOutputIsEmpty todo =
|
||||
Set.null todo.dependentsOfTodo
|
||||
&& defnsAreEmpty todo.directDependenciesWithoutNames
|
||||
&& Names.isEmpty todo.nameConflicts
|
||||
&& not todo.defnsInLib
|
||||
&& todo.incoherentDeclReasons == IncoherentDeclReasons [] [] [] []
|
||||
|
||||
data AmbiguousReset'Argument
|
||||
= AmbiguousReset'Hash
|
||||
|
@ -18,6 +18,7 @@ module Unison.Codebase.TranscriptParser
|
||||
)
|
||||
where
|
||||
|
||||
import CMark qualified
|
||||
import Control.Lens (use, (?~))
|
||||
import Crypto.Random qualified as Random
|
||||
import Data.Aeson qualified as Aeson
|
||||
@ -118,12 +119,14 @@ instance Show APIRequest where
|
||||
show (GetRequest txt) = "GET " <> Text.unpack txt
|
||||
show (APIComment txt) = "-- " <> Text.unpack txt
|
||||
|
||||
pattern CMarkCodeBlock :: (Maybe CMark.PosInfo) -> Text -> Text -> CMark.Node
|
||||
pattern CMarkCodeBlock pos info body = CMark.Node pos (CMark.CODE_BLOCK info body) []
|
||||
|
||||
data Stanza
|
||||
= Ucm Hidden ExpectingError [UcmLine]
|
||||
| Unison Hidden ExpectingError (Maybe ScratchFileName) Text
|
||||
| API [APIRequest]
|
||||
| UnprocessedFence FenceType Text
|
||||
| Unfenced Text
|
||||
| UnprocessedBlock CMark.Node
|
||||
|
||||
instance Show UcmLine where
|
||||
show = \case
|
||||
@ -133,43 +136,34 @@ instance Show UcmLine where
|
||||
showContext (UcmContextProject projectAndBranch) = Text.unpack (into @Text projectAndBranch)
|
||||
|
||||
instance Show Stanza where
|
||||
show s = case s of
|
||||
show s = (<> "\n") . Text.unpack . CMark.nodeToCommonmark [] Nothing $ stanzaToNode s
|
||||
|
||||
stanzaToNode :: Stanza -> CMark.Node
|
||||
stanzaToNode =
|
||||
\case
|
||||
Ucm _ _ cmds ->
|
||||
unlines
|
||||
[ "```ucm",
|
||||
foldl (\x y -> x ++ show y) "" cmds,
|
||||
"```"
|
||||
]
|
||||
CMarkCodeBlock Nothing "ucm" . Text.pack $
|
||||
foldl (\x y -> x ++ show y) "" cmds
|
||||
Unison _hide _ fname txt ->
|
||||
CMarkCodeBlock Nothing "unison" . Text.pack $
|
||||
unlines
|
||||
[ "```unison",
|
||||
case fname of
|
||||
Nothing -> Text.unpack txt <> "```\n"
|
||||
[ case fname of
|
||||
Nothing -> Text.unpack txt
|
||||
Just fname ->
|
||||
unlines
|
||||
[ "---",
|
||||
"title: " <> Text.unpack fname,
|
||||
"---",
|
||||
Text.unpack txt,
|
||||
"```",
|
||||
""
|
||||
Text.unpack txt
|
||||
]
|
||||
]
|
||||
API apiRequests ->
|
||||
"```api\n"
|
||||
<> ( apiRequests
|
||||
CMarkCodeBlock Nothing "api" . Text.pack $
|
||||
( apiRequests
|
||||
& fmap show
|
||||
& unlines
|
||||
)
|
||||
<> "```\n"
|
||||
UnprocessedFence typ txt ->
|
||||
unlines
|
||||
[ "```" <> Text.unpack typ,
|
||||
Text.unpack txt,
|
||||
"```",
|
||||
""
|
||||
]
|
||||
Unfenced txt -> Text.unpack txt
|
||||
UnprocessedBlock node -> node
|
||||
|
||||
parseFile :: FilePath -> IO (Either TranscriptError [Stanza])
|
||||
parseFile filePath = do
|
||||
@ -181,7 +175,7 @@ parseFile filePath = do
|
||||
else pure . Left . TranscriptParseError . Text.pack $ filePath <> " does not exist"
|
||||
|
||||
parse :: String -> Text -> Either TranscriptError [Stanza]
|
||||
parse srcName txt = case P.parse (stanzas <* P.eof) srcName txt of
|
||||
parse srcName txt = case stanzas srcName txt of
|
||||
Right a -> Right a
|
||||
Left e -> Left . TranscriptParseError . Text.pack . P.errorBundlePretty $ e
|
||||
|
||||
@ -333,7 +327,7 @@ run isTest verbosity dir stanzas codebase runtime sbRuntime nRuntime config ucmV
|
||||
for (reverse scratchFileUpdates) \(fp, contents) -> do
|
||||
let fenceDescription = "unison:added-by-ucm " <> fp
|
||||
-- Output blocks for any scratch file updates the ucm block triggered.
|
||||
Q.undequeue inputQueue (UnprocessedFence fenceDescription contents, Nothing)
|
||||
Q.undequeue inputQueue (UnprocessedBlock $ CMarkCodeBlock Nothing fenceDescription contents, Nothing)
|
||||
awaitInput
|
||||
-- ucm command to run
|
||||
Just (Just ucmLine) -> do
|
||||
@ -414,10 +408,7 @@ run isTest verbosity dir stanzas codebase runtime sbRuntime nRuntime config ucmV
|
||||
++ "."
|
||||
IO.hFlush IO.stdout
|
||||
case s of
|
||||
Unfenced _ -> do
|
||||
liftIO (output $ show s)
|
||||
awaitInput
|
||||
UnprocessedFence _ _ -> do
|
||||
UnprocessedBlock _ -> do
|
||||
liftIO (output $ show s)
|
||||
awaitInput
|
||||
Unison hide errOk filename txt -> do
|
||||
@ -435,7 +426,7 @@ run isTest verbosity dir stanzas codebase runtime sbRuntime nRuntime config ucmV
|
||||
API apiRequests -> do
|
||||
liftIO (output "``` api\n")
|
||||
liftIO (for_ apiRequests apiRequest)
|
||||
liftIO (output "```")
|
||||
liftIO (output "```\n\n")
|
||||
awaitInput
|
||||
Ucm hide errOk cmds -> do
|
||||
liftIO (writeIORef hidden hide)
|
||||
@ -588,8 +579,12 @@ transcriptFailure out msg = do
|
||||
|
||||
type P = P.Parsec Void Text
|
||||
|
||||
stanzas :: P [Stanza]
|
||||
stanzas = P.many (fenced <|> unfenced)
|
||||
stanzas :: String -> Text -> Either (P.ParseErrorBundle Text Void) [Stanza]
|
||||
stanzas srcName = (\(CMark.Node _ _DOCUMENT blocks) -> traverse stanzaFromBlock blocks) . CMark.commonmarkToNode []
|
||||
where
|
||||
stanzaFromBlock block = case block of
|
||||
CMarkCodeBlock _ info body -> fromMaybe (UnprocessedBlock block) <$> P.parse (fenced info) srcName body
|
||||
_ -> pure $ UnprocessedBlock block
|
||||
|
||||
ucmLine :: P UcmLine
|
||||
ucmLine = ucmCommand <|> ucmComment
|
||||
@ -630,18 +625,21 @@ apiRequest = do
|
||||
spaces
|
||||
pure (APIComment comment)
|
||||
|
||||
fenced :: P Stanza
|
||||
fenced = do
|
||||
fence
|
||||
-- | Produce the correct parser for the code block based on the provided info string.
|
||||
fenced :: Text -> P (Maybe Stanza)
|
||||
fenced info = do
|
||||
body <- P.getInput
|
||||
P.setInput info
|
||||
fenceType <- lineToken (word "ucm" <|> word "unison" <|> word "api" <|> language)
|
||||
stanza <-
|
||||
case fenceType of
|
||||
"ucm" -> do
|
||||
hide <- hidden
|
||||
err <- expectingError
|
||||
P.setInput body
|
||||
_ <- spaces
|
||||
cmds <- many ucmLine
|
||||
pure $ Ucm hide err cmds
|
||||
pure . pure $ Ucm hide err cmds
|
||||
"unison" ->
|
||||
do
|
||||
-- todo: this has to be more interesting
|
||||
@ -651,44 +649,17 @@ fenced = do
|
||||
hide <- lineToken hidden
|
||||
err <- lineToken expectingError
|
||||
fileName <- optional untilSpace1
|
||||
blob <- spaces *> untilFence
|
||||
pure $ Unison hide err fileName blob
|
||||
P.setInput body
|
||||
blob <- spaces *> (Text.init <$> P.getInput)
|
||||
pure . pure $ Unison hide err fileName blob
|
||||
"api" -> do
|
||||
P.setInput body
|
||||
_ <- spaces
|
||||
apiRequests <- many apiRequest
|
||||
pure $ API apiRequests
|
||||
_ -> UnprocessedFence fenceType <$> untilFence
|
||||
fence
|
||||
pure . pure $ API apiRequests
|
||||
_ -> pure Nothing
|
||||
pure stanza
|
||||
|
||||
-- Three backticks, consumes trailing spaces too
|
||||
-- ```
|
||||
fence :: P ()
|
||||
fence = P.try $ do void (word "```"); spaces
|
||||
|
||||
-- Parses up until next fence
|
||||
unfenced :: P Stanza
|
||||
unfenced = Unfenced <$> untilFence
|
||||
|
||||
untilFence :: P Text
|
||||
untilFence = do
|
||||
_ <- P.lookAhead (P.takeP Nothing 1)
|
||||
go mempty
|
||||
where
|
||||
go :: Seq Text -> P Text
|
||||
go !acc = do
|
||||
f <- P.lookAhead (P.optional fence)
|
||||
case f of
|
||||
Nothing -> do
|
||||
oneOrTwoBackticks <- optional (word' "``" <|> word' "`")
|
||||
let start = fromMaybe "" oneOrTwoBackticks
|
||||
txt <- P.takeWhileP (Just "unfenced") (/= '`')
|
||||
eof <- P.lookAhead (P.optional P.eof)
|
||||
case eof of
|
||||
Just _ -> pure $ fold (acc <> pure txt)
|
||||
Nothing -> go (acc <> pure start <> pure txt)
|
||||
Just _ -> pure $ fold acc
|
||||
|
||||
word' :: Text -> P Text
|
||||
word' txt = P.try $ do
|
||||
chs <- P.takeP (Just $ show txt) (Text.length txt)
|
||||
|
@ -85,6 +85,7 @@ import Unison.Hash32 (Hash32)
|
||||
import Unison.HashQualified qualified as HQ
|
||||
import Unison.HashQualified' qualified as HQ'
|
||||
import Unison.LabeledDependency as LD
|
||||
import Unison.Merge.DeclCoherencyCheck (IncoherentDeclReasons (..))
|
||||
import Unison.Name (Name)
|
||||
import Unison.Name qualified as Name
|
||||
import Unison.NameSegment qualified as NameSegment
|
||||
@ -1405,6 +1406,7 @@ notifyUser dir = \case
|
||||
<> "the same on both branches, or making neither of them a builtin, and then try the merge again."
|
||||
)
|
||||
]
|
||||
-- Note [ConstructorAliasMessage] If you change this, also change the other similar one
|
||||
MergeConstructorAlias aliceOrBob typeName conName1 conName2 ->
|
||||
pure . P.lines $
|
||||
[ P.wrap "Sorry, I wasn't able to perform the merge:",
|
||||
@ -1420,6 +1422,7 @@ notifyUser dir = \case
|
||||
"",
|
||||
P.wrap "Please delete all but one name for each constructor, and then try merging again."
|
||||
]
|
||||
-- Note [DefnsInLibMessage] If you change this, also change the other similar one
|
||||
MergeDefnsInLib aliceOrBob ->
|
||||
pure . P.lines $
|
||||
[ P.wrap "Sorry, I wasn't able to perform the merge:",
|
||||
@ -1432,6 +1435,7 @@ notifyUser dir = \case
|
||||
"",
|
||||
P.wrap "Please move or remove it and then try merging again."
|
||||
]
|
||||
-- Note [MissingConstructorNameMessage] If you change this, also change the other similar one
|
||||
MergeMissingConstructorName aliceOrBob name ->
|
||||
pure . P.lines $
|
||||
[ P.wrap "Sorry, I wasn't able to perform the merge:",
|
||||
@ -1450,6 +1454,7 @@ notifyUser dir = \case
|
||||
<> IP.makeExample IP.aliasTerm ["<hash>", prettyName name <> ".<ConstructorName>"]
|
||||
<> "to give names to each unnamed constructor, and then try the merge again."
|
||||
]
|
||||
-- Note [NestedDeclAliasMessage] If you change this, also change the other similar one
|
||||
MergeNestedDeclAlias aliceOrBob shorterName longerName ->
|
||||
pure . P.wrap $
|
||||
"On"
|
||||
@ -1460,6 +1465,7 @@ notifyUser dir = \case
|
||||
<> P.group (prettyName shorterName <> ".")
|
||||
<> "I'm not able to perform a merge when a type exists nested under an alias of itself. Please separate them or"
|
||||
<> "delete one copy, and then try merging again."
|
||||
-- Note [StrayConstructorMessage] If you change this, also change the other similar one
|
||||
MergeStrayConstructor aliceOrBob name ->
|
||||
pure . P.lines $
|
||||
[ P.wrap $
|
||||
@ -2523,20 +2529,20 @@ unsafePrettyTermResultSig' ppe = \case
|
||||
head (TypePrinter.prettySignaturesCT ppe [(r, name, typ)])
|
||||
_ -> error "Don't pass Nothing"
|
||||
|
||||
renderNameConflicts :: PPE.PrettyPrintEnv -> Names -> Numbered Pretty
|
||||
renderNameConflicts ppe conflictedNames = do
|
||||
renderNameConflicts :: Int -> Names -> Numbered Pretty
|
||||
renderNameConflicts hashLen conflictedNames = do
|
||||
let conflictedTypeNames :: Map Name [HQ.HashQualified Name]
|
||||
conflictedTypeNames =
|
||||
conflictedNames
|
||||
& Names.types
|
||||
& R.domain
|
||||
& fmap (foldMap (pure @[] . PPE.typeName ppe))
|
||||
& Map.mapWithKey \name -> map (HQ.take hashLen . HQ.HashQualified name . Reference.toShortHash) . Set.toList
|
||||
let conflictedTermNames :: Map Name [HQ.HashQualified Name]
|
||||
conflictedTermNames =
|
||||
conflictedNames
|
||||
& Names.terms
|
||||
& R.domain
|
||||
& fmap (foldMap (pure @[] . PPE.termName ppe))
|
||||
& Map.mapWithKey \name -> map (HQ.take hashLen . HQ.HashQualified name . Referent.toShortHash) . Set.toList
|
||||
let allConflictedNames :: [Name]
|
||||
allConflictedNames = Set.toList (Map.keysSet conflictedTermNames <> Map.keysSet conflictedTypeNames)
|
||||
prettyConflictedTypes <- showConflictedNames "type" conflictedTypeNames
|
||||
@ -2569,7 +2575,8 @@ renderNameConflicts ppe conflictedNames = do
|
||||
prettyConflicts <- for hashes \hash -> do
|
||||
n <- addNumberedArg $ SA.HashQualified hash
|
||||
pure $ formatNum n <> (P.blue . P.syntaxToColor . prettyHashQualified $ hash)
|
||||
pure . P.wrap $
|
||||
pure $
|
||||
P.wrap
|
||||
( "The "
|
||||
<> thingKind
|
||||
<> " "
|
||||
@ -2601,11 +2608,6 @@ handleTodoOutput :: TodoOutput -> Numbered Pretty
|
||||
handleTodoOutput todo
|
||||
| todoOutputIsEmpty todo = pure "You have no pending todo items. Good work! ✅"
|
||||
| otherwise = do
|
||||
prettyConflicts <-
|
||||
if todo.nameConflicts == mempty
|
||||
then pure mempty
|
||||
else renderNameConflicts todo.ppe.unsuffixifiedPPE todo.nameConflicts
|
||||
|
||||
prettyDependentsOfTodo <- do
|
||||
if Set.null todo.dependentsOfTodo
|
||||
then pure mempty
|
||||
@ -2654,11 +2656,159 @@ handleTodoOutput todo
|
||||
<> P.newline
|
||||
<> P.indentN 2 (P.lines types)
|
||||
|
||||
prettyConflicts <-
|
||||
if todo.nameConflicts == mempty
|
||||
then pure mempty
|
||||
else renderNameConflicts todo.hashLen todo.nameConflicts
|
||||
|
||||
let prettyDefnsInLib =
|
||||
if todo.defnsInLib
|
||||
then
|
||||
P.wrap $
|
||||
-- Note [DefnsInLibMessage] If you change this, also change the other similar one
|
||||
"There's a type or term at the top level of the `lib` namespace, where I only expect to find"
|
||||
<> "subnamespaces representing library dependencies. Please move or remove it."
|
||||
else mempty
|
||||
|
||||
prettyConstructorAliases <-
|
||||
let -- We want to filter out constructor aliases whose types are part of a "nested decl alias" problem, because
|
||||
-- otherwise we'd essentially be reporting those issues twice.
|
||||
--
|
||||
-- That is, if we have two nested aliases like
|
||||
--
|
||||
-- Foo = #XYZ
|
||||
-- Foo.Bar = #XYZ#0
|
||||
--
|
||||
-- Foo.inner.Alias = #XYZ
|
||||
-- Foo.inner.Alias.Constructor = #XYZ#0
|
||||
--
|
||||
-- then we'd prefer to say "oh no Foo and Foo.inner.Alias are aliases" but *not* additionally say "oh no
|
||||
-- Foo.Bar and Foo.inner.Alias.Constructor are aliases".
|
||||
notNestedDeclAlias (typeName, _, _) =
|
||||
foldr
|
||||
(\(short, long) acc -> typeName /= short && typeName /= long && acc)
|
||||
True
|
||||
todo.incoherentDeclReasons.nestedDeclAliases
|
||||
in case filter notNestedDeclAlias todo.incoherentDeclReasons.constructorAliases of
|
||||
[] -> pure mempty
|
||||
aliases -> do
|
||||
things <-
|
||||
for aliases \(typeName, conName1, conName2) -> do
|
||||
n1 <- addNumberedArg (SA.Name conName1)
|
||||
n2 <- addNumberedArg (SA.Name conName2)
|
||||
pure (typeName, formatNum n1 <> prettyName conName1, formatNum n2 <> prettyName conName2)
|
||||
pure $
|
||||
things
|
||||
& map
|
||||
( \(typeName, prettyCon1, prettyCon2) ->
|
||||
-- Note [ConstructorAliasMessage] If you change this, also change the other similar one
|
||||
P.wrap ("The type" <> prettyName typeName <> "has a constructor with multiple names.")
|
||||
<> P.newline
|
||||
<> P.newline
|
||||
<> P.indentN 2 (P.lines [prettyCon1, prettyCon2])
|
||||
<> P.newline
|
||||
<> P.newline
|
||||
<> P.wrap "Please delete all but one name for each constructor."
|
||||
)
|
||||
& P.sep "\n\n"
|
||||
|
||||
prettyMissingConstructorNames <-
|
||||
case NEList.nonEmpty todo.incoherentDeclReasons.missingConstructorNames of
|
||||
Nothing -> pure mempty
|
||||
Just types0 -> do
|
||||
stuff <-
|
||||
for types0 \typ -> do
|
||||
n <- addNumberedArg (SA.Name typ)
|
||||
pure (n, typ)
|
||||
-- Note [MissingConstructorNameMessage] If you change this, also change the other similar one
|
||||
pure $
|
||||
P.wrap
|
||||
"These types have some constructors with missing names."
|
||||
<> P.newline
|
||||
<> P.newline
|
||||
<> P.indentN 2 (P.lines (fmap (\(n, typ) -> formatNum n <> prettyName typ) stuff))
|
||||
<> P.newline
|
||||
<> P.newline
|
||||
<> P.wrap
|
||||
( "You can use"
|
||||
<> IP.makeExample
|
||||
IP.view
|
||||
[ let firstNum = fst (NEList.head stuff)
|
||||
lastNum = fst (NEList.last stuff)
|
||||
in if firstNum == lastNum
|
||||
then P.string (show firstNum)
|
||||
else P.string (show firstNum) <> "-" <> P.string (show lastNum)
|
||||
]
|
||||
<> "and"
|
||||
<> IP.makeExample IP.aliasTerm ["<hash>", "<TypeName>.<ConstructorName>"]
|
||||
<> "to give names to each unnamed constructor."
|
||||
)
|
||||
|
||||
prettyNestedDeclAliases <-
|
||||
case todo.incoherentDeclReasons.nestedDeclAliases of
|
||||
[] -> pure mempty
|
||||
aliases0 -> do
|
||||
aliases1 <-
|
||||
for aliases0 \(short, long) -> do
|
||||
n1 <- addNumberedArg (SA.Name short)
|
||||
n2 <- addNumberedArg (SA.Name long)
|
||||
pure (formatNum n1 <> prettyName short, formatNum n2 <> prettyName long)
|
||||
-- Note [NestedDeclAliasMessage] If you change this, also change the other similar one
|
||||
pure $
|
||||
aliases1
|
||||
& map
|
||||
( \(short, long) ->
|
||||
P.wrap
|
||||
( "These types are aliases, but one is nested under the other. Please separate them or delete"
|
||||
<> "one copy."
|
||||
)
|
||||
<> P.newline
|
||||
<> P.newline
|
||||
<> P.indentN 2 (P.lines [short, long])
|
||||
)
|
||||
& P.sep "\n\n"
|
||||
|
||||
prettyStrayConstructors <-
|
||||
case todo.incoherentDeclReasons.strayConstructors of
|
||||
[] -> pure mempty
|
||||
constructors -> do
|
||||
nums <-
|
||||
for constructors \constructor -> do
|
||||
addNumberedArg (SA.Name constructor)
|
||||
-- Note [StrayConstructorMessage] If you change this, also change the other similar one
|
||||
pure $
|
||||
P.wrap "These constructors are not nested beneath their corresponding type names:"
|
||||
<> P.newline
|
||||
<> P.newline
|
||||
<> P.indentN
|
||||
2
|
||||
( P.lines
|
||||
( zipWith
|
||||
(\n constructor -> formatNum n <> prettyName constructor)
|
||||
nums
|
||||
constructors
|
||||
)
|
||||
)
|
||||
<> P.newline
|
||||
<> P.newline
|
||||
<> P.wrap
|
||||
( "For each one, please either use"
|
||||
<> IP.makeExample' IP.moveAll
|
||||
<> "to move if, or if it's an extra copy, you can simply"
|
||||
<> IP.makeExample' IP.delete
|
||||
<> "it."
|
||||
)
|
||||
|
||||
(pure . P.sep "\n\n" . P.nonEmpty)
|
||||
[ prettyDependentsOfTodo,
|
||||
prettyDirectTermDependenciesWithoutNames,
|
||||
prettyDirectTypeDependenciesWithoutNames,
|
||||
prettyConflicts
|
||||
prettyConflicts,
|
||||
prettyDefnsInLib,
|
||||
prettyConstructorAliases,
|
||||
prettyMissingConstructorNames,
|
||||
prettyNestedDeclAliases,
|
||||
prettyStrayConstructors
|
||||
]
|
||||
|
||||
listOfDefinitions ::
|
||||
|
@ -197,6 +197,7 @@ library
|
||||
, base
|
||||
, bytes
|
||||
, bytestring
|
||||
, cmark
|
||||
, co-log-core
|
||||
, code-page
|
||||
, concurrent-output
|
||||
@ -338,6 +339,7 @@ executable transcripts
|
||||
, base
|
||||
, bytes
|
||||
, bytestring
|
||||
, cmark
|
||||
, co-log-core
|
||||
, code-page
|
||||
, concurrent-output
|
||||
@ -486,6 +488,7 @@ test-suite cli-tests
|
||||
, base
|
||||
, bytes
|
||||
, bytestring
|
||||
, cmark
|
||||
, co-log-core
|
||||
, code-page
|
||||
, concurrent-output
|
||||
|
@ -66,6 +66,7 @@ default-extensions:
|
||||
- DerivingStrategies
|
||||
- DerivingVia
|
||||
- DoAndIfThenElse
|
||||
- DuplicateRecordFields
|
||||
- FlexibleContexts
|
||||
- FlexibleInstances
|
||||
- GADTs
|
||||
|
@ -49,12 +49,15 @@ module Unison.Names
|
||||
hashQualifyTypesRelation,
|
||||
hashQualifyTermsRelation,
|
||||
fromTermsAndTypes,
|
||||
lenientToNametree,
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Map qualified as Map
|
||||
import Data.Semialign (alignWith)
|
||||
import Data.Set qualified as Set
|
||||
import Data.Text qualified as Text
|
||||
import Data.These (These (..))
|
||||
import Text.FuzzyFind qualified as FZF
|
||||
import Unison.ConstructorReference (GConstructorReference (..))
|
||||
import Unison.ConstructorType qualified as CT
|
||||
@ -64,6 +67,7 @@ import Unison.LabeledDependency (LabeledDependency)
|
||||
import Unison.LabeledDependency qualified as LD
|
||||
import Unison.Name (Name)
|
||||
import Unison.Name qualified as Name
|
||||
import Unison.NameSegment (NameSegment)
|
||||
import Unison.Prelude
|
||||
import Unison.Reference (Reference, TermReference, TypeReference)
|
||||
import Unison.Reference qualified as Reference
|
||||
@ -71,6 +75,10 @@ import Unison.Referent (Referent)
|
||||
import Unison.Referent qualified as Referent
|
||||
import Unison.ShortHash (ShortHash)
|
||||
import Unison.ShortHash qualified as SH
|
||||
import Unison.Util.BiMultimap (BiMultimap)
|
||||
import Unison.Util.BiMultimap qualified as BiMultimap
|
||||
import Unison.Util.Defns (Defns (..), DefnsF)
|
||||
import Unison.Util.Nametree (Nametree, unflattenNametree)
|
||||
import Unison.Util.Relation (Relation)
|
||||
import Unison.Util.Relation qualified as R
|
||||
import Unison.Util.Relation qualified as Relation
|
||||
@ -95,7 +103,7 @@ instance Monoid (Names) where
|
||||
mempty = Names mempty mempty
|
||||
|
||||
isEmpty :: Names -> Bool
|
||||
isEmpty n = R.null (terms n) && R.null (types n)
|
||||
isEmpty n = R.null n.terms && R.null n.types
|
||||
|
||||
map :: (Name -> Name) -> Names -> Names
|
||||
map f (Names {terms, types}) = Names terms' types'
|
||||
@ -122,8 +130,8 @@ fuzzyFind nameToText query names =
|
||||
. Prelude.filter prefilter
|
||||
. Map.toList
|
||||
-- `mapMonotonic` is safe here and saves a log n factor
|
||||
$ (Set.mapMonotonic Left <$> R.toMultimap (terms names))
|
||||
<> (Set.mapMonotonic Right <$> R.toMultimap (types names))
|
||||
$ (Set.mapMonotonic Left <$> R.toMultimap names.terms)
|
||||
<> (Set.mapMonotonic Right <$> R.toMultimap names.types)
|
||||
where
|
||||
lowerqueryt = Text.toLower . Text.pack <$> query
|
||||
-- For performance, case-insensitive substring matching as a pre-filter
|
||||
@ -250,8 +258,8 @@ unionLeft' ::
|
||||
Names
|
||||
unionLeft' shouldOmit a b = Names terms' types'
|
||||
where
|
||||
terms' = foldl' go (terms a) (R.toList $ terms b)
|
||||
types' = foldl' go (types a) (R.toList $ types b)
|
||||
terms' = foldl' go a.terms (R.toList b.terms)
|
||||
types' = foldl' go a.types (R.toList b.types)
|
||||
go :: (Ord a, Ord b) => Relation a b -> (a, b) -> Relation a b
|
||||
go acc (n, r) = if shouldOmit n r acc then acc else R.insert n r acc
|
||||
|
||||
@ -260,7 +268,7 @@ numHashChars :: Int
|
||||
numHashChars = 3
|
||||
|
||||
termsNamed :: Names -> Name -> Set Referent
|
||||
termsNamed = flip R.lookupDom . terms
|
||||
termsNamed = flip R.lookupDom . (.terms)
|
||||
|
||||
-- | Get all terms with a specific name.
|
||||
refTermsNamed :: Names -> Name -> Set TermReference
|
||||
@ -281,13 +289,13 @@ refTermsHQNamed names = \case
|
||||
in Set.mapMaybe f (termsNamed names name)
|
||||
|
||||
typesNamed :: Names -> Name -> Set TypeReference
|
||||
typesNamed = flip R.lookupDom . types
|
||||
typesNamed = flip R.lookupDom . (.types)
|
||||
|
||||
namesForReferent :: Names -> Referent -> Set Name
|
||||
namesForReferent names r = R.lookupRan r (terms names)
|
||||
namesForReferent names r = R.lookupRan r names.terms
|
||||
|
||||
namesForReference :: Names -> TypeReference -> Set Name
|
||||
namesForReference names r = R.lookupRan r (types names)
|
||||
namesForReference names r = R.lookupRan r names.types
|
||||
|
||||
termAliases :: Names -> Name -> Referent -> Set Name
|
||||
termAliases names n r = Set.delete n $ namesForReferent names r
|
||||
@ -422,20 +430,20 @@ filterTypes f (Names terms types) = Names terms (R.filterDom f types)
|
||||
difference :: Names -> Names -> Names
|
||||
difference a b =
|
||||
Names
|
||||
(R.difference (terms a) (terms b))
|
||||
(R.difference (types a) (types b))
|
||||
(R.difference a.terms b.terms)
|
||||
(R.difference a.types b.types)
|
||||
|
||||
contains :: Names -> Reference -> Bool
|
||||
contains names =
|
||||
-- We want to compute `termsReferences` only once, if `contains` is partially applied to a `Names`, and called over
|
||||
-- and over for different references. GHC would probably float `termsReferences` out without the explicit lambda, but
|
||||
-- it's written like this just to be sure.
|
||||
\r -> Set.member r termsReferences || R.memberRan r (types names)
|
||||
\r -> Set.member r termsReferences || R.memberRan r names.types
|
||||
where
|
||||
-- this check makes `contains` O(n) instead of O(log n)
|
||||
termsReferences :: Set TermReference
|
||||
termsReferences =
|
||||
Set.map Referent.toReference (R.ran (terms names))
|
||||
Set.map Referent.toReference (R.ran names.terms)
|
||||
|
||||
-- | filters out everything from the domain except what's conflicted
|
||||
conflicts :: Names -> Names
|
||||
@ -448,9 +456,9 @@ conflicts Names {..} = Names (R.filterManyDom terms) (R.filterManyDom types)
|
||||
-- See usage in `FileParser` for handling precendence of symbol
|
||||
-- resolution where local names are preferred to codebase names.
|
||||
shadowTerms :: [Name] -> Names -> Names
|
||||
shadowTerms ns n0 = Names terms' (types n0)
|
||||
shadowTerms ns n0 = Names terms' n0.types
|
||||
where
|
||||
terms' = foldl' go (terms n0) ns
|
||||
terms' = foldl' go n0.terms ns
|
||||
go ts name = R.deleteDom name ts
|
||||
|
||||
-- | Given a mapping from name to qualified name, update a `Names`,
|
||||
@ -461,8 +469,8 @@ shadowTerms ns n0 = Names terms' (types n0)
|
||||
importing :: [(Name, Name)] -> Names -> Names
|
||||
importing shortToLongName ns =
|
||||
Names
|
||||
(foldl' go (terms ns) shortToLongName)
|
||||
(foldl' go (types ns) shortToLongName)
|
||||
(foldl' go ns.terms shortToLongName)
|
||||
(foldl' go ns.types shortToLongName)
|
||||
where
|
||||
go :: (Ord r) => Relation Name r -> (Name, Name) -> Relation Name r
|
||||
go m (shortname, qname) = case Name.searchByRankedSuffix qname m of
|
||||
@ -476,8 +484,8 @@ importing shortToLongName ns =
|
||||
-- `[(foo, io.foo), (bar, io.bar)]`.
|
||||
expandWildcardImport :: Name -> Names -> [(Name, Name)]
|
||||
expandWildcardImport prefix ns =
|
||||
[(suffix, full) | Just (suffix, full) <- go <$> R.toList (terms ns)]
|
||||
<> [(suffix, full) | Just (suffix, full) <- go <$> R.toList (types ns)]
|
||||
[(suffix, full) | Just (suffix, full) <- go <$> R.toList ns.terms]
|
||||
<> [(suffix, full) | Just (suffix, full) <- go <$> R.toList ns.types]
|
||||
where
|
||||
go :: (Name, a) -> Maybe (Name, Name)
|
||||
go (full, _) = do
|
||||
@ -498,7 +506,7 @@ constructorsForType r ns =
|
||||
possibleDatas = [Referent.Con (ConstructorReference r cid) CT.Data | cid <- [0 ..]]
|
||||
possibleEffects = [Referent.Con (ConstructorReference r cid) CT.Effect | cid <- [0 ..]]
|
||||
trim [] = []
|
||||
trim (h : t) = case R.lookupRan h (terms ns) of
|
||||
trim (h : t) = case R.lookupRan h ns.terms of
|
||||
s
|
||||
| Set.null s -> []
|
||||
| otherwise -> [(n, h) | n <- toList s] ++ trim t
|
||||
@ -517,3 +525,29 @@ hashQualifyRelation fromNamedRef rel = R.map go rel
|
||||
if Set.size (R.lookupDom n rel) > 1
|
||||
then (HQ.take numHashChars $ fromNamedRef n r, r)
|
||||
else (HQ.NameOnly n, r)
|
||||
|
||||
-- | "Leniently" view a Names as a NameTree
|
||||
--
|
||||
-- This function is "lenient" in the sense that it does not handle conflicted names with any smarts whatsoever. The
|
||||
-- resulting nametree will simply contain one of the associated references of a conflicted name - we don't specify
|
||||
-- which.
|
||||
lenientToNametree :: Names -> Nametree (DefnsF (Map NameSegment) Referent TypeReference)
|
||||
lenientToNametree names =
|
||||
alignWith
|
||||
( \case
|
||||
This terms -> Defns {terms, types = Map.empty}
|
||||
That types -> Defns {terms = Map.empty, types}
|
||||
These terms types -> Defns {terms, types}
|
||||
)
|
||||
(lenientRelationToNametree names.terms)
|
||||
(lenientRelationToNametree names.types)
|
||||
where
|
||||
lenientRelationToNametree :: Ord a => Relation Name a -> Nametree (Map NameSegment a)
|
||||
lenientRelationToNametree =
|
||||
unflattenNametree . lenientRelationToLeftUniqueRelation
|
||||
|
||||
lenientRelationToLeftUniqueRelation :: (Ord a, Ord b) => Relation a b -> BiMultimap b a
|
||||
lenientRelationToLeftUniqueRelation =
|
||||
-- The partial `Set.findMin` are fine here because Relation.domain only has non-empty Set values. A NESet would be
|
||||
-- better.
|
||||
BiMultimap.fromRange . Map.map Set.findMin . Relation.domain
|
||||
|
@ -72,6 +72,7 @@ library
|
||||
DerivingStrategies
|
||||
DerivingVia
|
||||
DoAndIfThenElse
|
||||
DuplicateRecordFields
|
||||
FlexibleContexts
|
||||
FlexibleInstances
|
||||
GADTs
|
||||
@ -140,6 +141,7 @@ test-suite tests
|
||||
DerivingStrategies
|
||||
DerivingVia
|
||||
DoAndIfThenElse
|
||||
DuplicateRecordFields
|
||||
FlexibleContexts
|
||||
FlexibleInstances
|
||||
GADTs
|
||||
|
@ -83,15 +83,17 @@ module Unison.Merge.DeclCoherencyCheck
|
||||
( IncoherentDeclReason (..),
|
||||
checkDeclCoherency,
|
||||
lenientCheckDeclCoherency,
|
||||
|
||||
-- * Getting all failures rather than just the first
|
||||
IncoherentDeclReasons (..),
|
||||
checkAllDeclCoherency,
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Lens ((%=), (.=), _2)
|
||||
import Control.Monad.Except (ExceptT)
|
||||
import Control.Monad.Except qualified as Except
|
||||
import Control.Monad.State.Strict (StateT)
|
||||
import Control.Monad.State.Strict qualified as State
|
||||
import Control.Monad.Trans.Except qualified as Except (except)
|
||||
import Data.Functor.Compose (Compose (..))
|
||||
import Data.IntMap.Strict (IntMap)
|
||||
import Data.IntMap.Strict qualified as IntMap
|
||||
@ -136,72 +138,180 @@ checkDeclCoherency ::
|
||||
(TypeReferenceId -> m Int) ->
|
||||
Nametree (DefnsF (Map NameSegment) Referent TypeReference) ->
|
||||
m (Either IncoherentDeclReason DeclNameLookup)
|
||||
checkDeclCoherency loadDeclNumConstructors =
|
||||
checkDeclCoherency loadDeclNumConstructors nametree =
|
||||
Except.runExceptT
|
||||
. fmap (view #declNameLookup)
|
||||
( checkDeclCoherencyWith
|
||||
(lift . loadDeclNumConstructors)
|
||||
OnIncoherentDeclReasons
|
||||
{ onConstructorAlias = \x y z -> Except.throwError (IncoherentDeclReason'ConstructorAlias x y z), -- :: Name -> Name -> Name -> m (),
|
||||
onMissingConstructorName = \x -> Except.throwError (IncoherentDeclReason'MissingConstructorName x), -- :: Name -> m (),
|
||||
onNestedDeclAlias = \x y -> Except.throwError (IncoherentDeclReason'NestedDeclAlias x y), -- :: Name -> Name -> m (),
|
||||
onStrayConstructor = \x -> Except.throwError (IncoherentDeclReason'StrayConstructor x) -- :: Name -> m ()
|
||||
}
|
||||
nametree
|
||||
)
|
||||
|
||||
data IncoherentDeclReasons = IncoherentDeclReasons
|
||||
{ constructorAliases :: ![(Name, Name, Name)],
|
||||
missingConstructorNames :: ![Name],
|
||||
nestedDeclAliases :: ![(Name, Name)],
|
||||
strayConstructors :: ![Name]
|
||||
}
|
||||
deriving stock (Eq, Generic)
|
||||
|
||||
-- | Like 'checkDeclCoherency', but returns info about all of the incoherent decls found, not just the first.
|
||||
checkAllDeclCoherency ::
|
||||
forall m.
|
||||
Monad m =>
|
||||
(TypeReferenceId -> m Int) ->
|
||||
Nametree (DefnsF (Map NameSegment) Referent TypeReference) ->
|
||||
m (Either IncoherentDeclReasons DeclNameLookup)
|
||||
checkAllDeclCoherency loadDeclNumConstructors nametree = do
|
||||
State.runStateT doCheck emptyReasons <&> \(declNameLookup, reasons) ->
|
||||
if reasons == emptyReasons
|
||||
then Right declNameLookup
|
||||
else Left (reverseReasons reasons)
|
||||
where
|
||||
doCheck :: StateT IncoherentDeclReasons m DeclNameLookup
|
||||
doCheck =
|
||||
checkDeclCoherencyWith
|
||||
(lift . loadDeclNumConstructors)
|
||||
( OnIncoherentDeclReasons
|
||||
{ onConstructorAlias = \x y z -> #constructorAliases %= ((x, y, z) :),
|
||||
onMissingConstructorName = \x -> #missingConstructorNames %= (x :),
|
||||
onNestedDeclAlias = \x y -> #nestedDeclAliases %= ((x, y) :),
|
||||
onStrayConstructor = \x -> #strayConstructors %= (x :)
|
||||
}
|
||||
)
|
||||
nametree
|
||||
|
||||
emptyReasons :: IncoherentDeclReasons
|
||||
emptyReasons =
|
||||
IncoherentDeclReasons [] [] [] []
|
||||
|
||||
reverseReasons :: IncoherentDeclReasons -> IncoherentDeclReasons
|
||||
reverseReasons reasons =
|
||||
IncoherentDeclReasons
|
||||
{ constructorAliases = reverse reasons.constructorAliases,
|
||||
missingConstructorNames = reverse reasons.missingConstructorNames,
|
||||
nestedDeclAliases = reverse reasons.nestedDeclAliases,
|
||||
strayConstructors = reverse reasons.strayConstructors
|
||||
}
|
||||
|
||||
data OnIncoherentDeclReasons m = OnIncoherentDeclReasons
|
||||
{ onConstructorAlias :: Name -> Name -> Name -> m (),
|
||||
onMissingConstructorName :: Name -> m (),
|
||||
onNestedDeclAlias :: Name -> Name -> m (),
|
||||
onStrayConstructor :: Name -> m ()
|
||||
}
|
||||
|
||||
checkDeclCoherencyWith ::
|
||||
forall m.
|
||||
Monad m =>
|
||||
(TypeReferenceId -> m Int) ->
|
||||
OnIncoherentDeclReasons m ->
|
||||
Nametree (DefnsF (Map NameSegment) Referent TypeReference) ->
|
||||
m DeclNameLookup
|
||||
checkDeclCoherencyWith loadDeclNumConstructors callbacks =
|
||||
fmap (view #declNameLookup)
|
||||
. (`State.execStateT` DeclCoherencyCheckState Map.empty (DeclNameLookup Map.empty Map.empty))
|
||||
. go []
|
||||
where
|
||||
go ::
|
||||
[NameSegment] ->
|
||||
(Nametree (DefnsF (Map NameSegment) Referent TypeReference)) ->
|
||||
StateT DeclCoherencyCheckState (ExceptT IncoherentDeclReason m) ()
|
||||
StateT DeclCoherencyCheckState m ()
|
||||
go prefix (Nametree defns children) = do
|
||||
for_ (Map.toList defns.terms) \case
|
||||
for_ (Map.toList defns.terms) (checkDeclCoherencyWith_DoTerms callbacks prefix)
|
||||
childrenWeWentInto <-
|
||||
forMaybe
|
||||
(Map.toList defns.types)
|
||||
(checkDeclCoherencyWith_DoTypes loadDeclNumConstructors callbacks go prefix children)
|
||||
let childrenWeHaventGoneInto = children `Map.withoutKeys` Set.fromList childrenWeWentInto
|
||||
for_ (Map.toList childrenWeHaventGoneInto) \(name, child) -> go (name : prefix) child
|
||||
|
||||
checkDeclCoherencyWith_DoTerms ::
|
||||
forall m.
|
||||
Monad m =>
|
||||
OnIncoherentDeclReasons m ->
|
||||
[NameSegment] ->
|
||||
(NameSegment, Referent) ->
|
||||
StateT DeclCoherencyCheckState m ()
|
||||
checkDeclCoherencyWith_DoTerms callbacks prefix = \case
|
||||
(_, Referent.Ref _) -> pure ()
|
||||
(_, Referent.Con (ConstructorReference (ReferenceBuiltin _) _) _) -> pure ()
|
||||
(name, Referent.Con (ConstructorReference (ReferenceDerived typeRef) conId) _) -> do
|
||||
DeclCoherencyCheckState {expectedConstructors} <- State.get
|
||||
expectedConstructors1 <- lift (Except.except (Map.upsertF f typeRef expectedConstructors))
|
||||
state <- State.get
|
||||
whenJustM (lift (runMaybeT (Map.upsertF f typeRef state.expectedConstructors))) \expectedConstructors1 ->
|
||||
#expectedConstructors .= expectedConstructors1
|
||||
where
|
||||
f ::
|
||||
Maybe (Name, ConstructorNames) ->
|
||||
Either IncoherentDeclReason (Name, ConstructorNames)
|
||||
f :: Maybe (Name, ConstructorNames) -> MaybeT m (Name, ConstructorNames)
|
||||
f = \case
|
||||
Nothing -> Left (IncoherentDeclReason'StrayConstructor name1)
|
||||
Nothing -> do
|
||||
lift (callbacks.onStrayConstructor name1)
|
||||
MaybeT (pure Nothing)
|
||||
Just (typeName, expected) ->
|
||||
case recordConstructorName conId name1 expected of
|
||||
Left existingName -> Left (IncoherentDeclReason'ConstructorAlias typeName existingName name1)
|
||||
Right expected1 -> Right (typeName, expected1)
|
||||
Left existingName -> do
|
||||
lift (callbacks.onConstructorAlias typeName existingName name1)
|
||||
MaybeT (pure Nothing)
|
||||
Right expected1 -> pure (typeName, expected1)
|
||||
where
|
||||
name1 = fullName name
|
||||
name1 =
|
||||
Name.fromReverseSegments (name :| prefix)
|
||||
|
||||
childrenWeWentInto <-
|
||||
forMaybe (Map.toList defns.types) \case
|
||||
checkDeclCoherencyWith_DoTypes ::
|
||||
forall m.
|
||||
Monad m =>
|
||||
(TypeReferenceId -> m Int) ->
|
||||
OnIncoherentDeclReasons m ->
|
||||
( [NameSegment] ->
|
||||
(Nametree (DefnsF (Map NameSegment) Referent TypeReference)) ->
|
||||
StateT DeclCoherencyCheckState m ()
|
||||
) ->
|
||||
[NameSegment] ->
|
||||
Map NameSegment (Nametree (DefnsF (Map NameSegment) Referent TypeReference)) ->
|
||||
(NameSegment, TypeReference) ->
|
||||
StateT DeclCoherencyCheckState m (Maybe NameSegment)
|
||||
checkDeclCoherencyWith_DoTypes loadDeclNumConstructors callbacks go prefix children = \case
|
||||
(_, ReferenceBuiltin _) -> pure Nothing
|
||||
(name, ReferenceDerived typeRef) -> do
|
||||
DeclCoherencyCheckState {expectedConstructors} <- State.get
|
||||
whatHappened <- do
|
||||
state <- State.get
|
||||
maybeWhatHappened <- do
|
||||
let recordNewDecl ::
|
||||
Maybe (Name, ConstructorNames) ->
|
||||
Compose (ExceptT IncoherentDeclReason m) WhatHappened (Name, ConstructorNames)
|
||||
Compose (MaybeT m) WhatHappened (Name, ConstructorNames)
|
||||
recordNewDecl =
|
||||
Compose . \case
|
||||
Just (shorterTypeName, _) -> Except.throwError (IncoherentDeclReason'NestedDeclAlias shorterTypeName typeName)
|
||||
Just (shorterTypeName, _) -> do
|
||||
lift (callbacks.onNestedDeclAlias shorterTypeName typeName)
|
||||
MaybeT (pure Nothing)
|
||||
Nothing ->
|
||||
lift (loadDeclNumConstructors typeRef) <&> \case
|
||||
0 -> UninhabitedDecl
|
||||
n -> InhabitedDecl (typeName, emptyConstructorNames n)
|
||||
lift (getCompose (Map.upsertF recordNewDecl typeRef expectedConstructors))
|
||||
case whatHappened of
|
||||
UninhabitedDecl -> do
|
||||
lift (runMaybeT (getCompose (Map.upsertF recordNewDecl typeRef state.expectedConstructors)))
|
||||
case maybeWhatHappened of
|
||||
Nothing -> pure Nothing
|
||||
Just UninhabitedDecl -> do
|
||||
#declNameLookup . #declToConstructors %= Map.insert typeName []
|
||||
pure Nothing
|
||||
InhabitedDecl expectedConstructors1 -> do
|
||||
child <-
|
||||
Map.lookup name children & onNothing do
|
||||
Except.throwError (IncoherentDeclReason'MissingConstructorName typeName)
|
||||
Just (InhabitedDecl expectedConstructors1) -> do
|
||||
case Map.lookup name children of
|
||||
Nothing -> do
|
||||
lift (callbacks.onMissingConstructorName typeName)
|
||||
pure Nothing
|
||||
Just child -> do
|
||||
#expectedConstructors .= expectedConstructors1
|
||||
go (name : prefix) child
|
||||
DeclCoherencyCheckState {expectedConstructors} <- State.get
|
||||
state <- State.get
|
||||
-- fromJust is safe here because we upserted `typeRef` key above
|
||||
let (fromJust -> (_typeName, maybeConstructorNames), expectedConstructors1) =
|
||||
Map.deleteLookup typeRef expectedConstructors
|
||||
constructorNames <-
|
||||
sequence (IntMap.elems maybeConstructorNames) & onNothing do
|
||||
Except.throwError (IncoherentDeclReason'MissingConstructorName typeName)
|
||||
Map.deleteLookup typeRef state.expectedConstructors
|
||||
#expectedConstructors .= expectedConstructors1
|
||||
case sequence (IntMap.elems maybeConstructorNames) of
|
||||
Nothing -> lift (callbacks.onMissingConstructorName typeName)
|
||||
Just constructorNames -> do
|
||||
#declNameLookup . #constructorToDecl %= \constructorToDecl ->
|
||||
List.foldl'
|
||||
(\acc constructorName -> Map.insert constructorName typeName acc)
|
||||
@ -210,12 +320,7 @@ checkDeclCoherency loadDeclNumConstructors =
|
||||
#declNameLookup . #declToConstructors %= Map.insert typeName constructorNames
|
||||
pure (Just name)
|
||||
where
|
||||
typeName = fullName name
|
||||
|
||||
let childrenWeHaventGoneInto = children `Map.withoutKeys` Set.fromList childrenWeWentInto
|
||||
for_ (Map.toList childrenWeHaventGoneInto) \(name, child) -> go (name : prefix) child
|
||||
where
|
||||
fullName name =
|
||||
typeName =
|
||||
Name.fromReverseSegments (name :| prefix)
|
||||
|
||||
-- | A lenient variant of 'checkDeclCoherency' - so lenient it can't even fail! It returns partial decl name lookup,
|
||||
|
@ -17,6 +17,7 @@ runtime-tests/selected> run.native tests.jit.only
|
||||
Per Dan:
|
||||
It's testing a flaw in how we were sending code from a scratch file to the native runtime, when that happened multiple times.
|
||||
Related to the verifiable refs and recursive functions.
|
||||
|
||||
``` unison
|
||||
foo = do
|
||||
go : Nat ->{Exception} ()
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
## Structural find and replace
|
||||
|
||||
Here's a scratch file with some rewrite rules:
|
||||
|
@ -30,7 +30,7 @@ scratch/a1> edit 1-1000
|
||||
definitions currently in this namespace.
|
||||
|
||||
```
|
||||
```unison:added-by-ucm scratch.u
|
||||
````` unison:added-by-ucm scratch.u
|
||||
structural ability Abort where abort : {Abort} a
|
||||
|
||||
structural ability Ask a where ask : {Ask a} a
|
||||
@ -766,7 +766,7 @@ UUID.randomUUIDBytes = do
|
||||
|
||||
(|>) : a -> (a ->{e} b) ->{e} b
|
||||
a |> f = f a
|
||||
```
|
||||
`````
|
||||
|
||||
This diff should be empty if the two namespaces are equivalent. If it's nonempty, the diff will show us the hashes that differ.
|
||||
|
||||
@ -795,7 +795,7 @@ scratch/a3> edit 1-5000
|
||||
definitions currently in this namespace.
|
||||
|
||||
```
|
||||
```unison:added-by-ucm scratch.u
|
||||
```` unison:added-by-ucm scratch.u
|
||||
explanationOfThisFile : Text
|
||||
explanationOfThisFile =
|
||||
"""
|
||||
@ -815,7 +815,7 @@ sloppyDocEval =
|
||||
1 + 1
|
||||
```
|
||||
}}
|
||||
```
|
||||
````
|
||||
|
||||
These are currently all expected to have different hashes on round trip.
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Test for code serialization operations.
|
||||
|
||||
Define a function, serialize it, then deserialize it back to an actual
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
Unison documentation is written in Unison and has some neat features:
|
||||
|
||||
* The documentation type provides a rich vocabulary of elements that go beyond markdown, including asides, callouts, tooltips, and more.
|
||||
* Docs may contain Unison code which is parsed and typechecked to ensure validity. No more out of date examples that don't compile or assume a bunch of implicit context!
|
||||
* Embeded examples are live and can show the results of evaluation. This uses the same evaluation cache as Unison's scratch files, allowing Unison docs to function like well-commented spreadsheets or notebooks.
|
||||
* Links to other definitions are typechecked to ensure they point to valid definitions. The links are resolved to hashes and won't be broken by name changes or moving definitions around.
|
||||
* Docs can be included in other docs and you can assemble documentation programmatically, using Unison code.
|
||||
* There's a powerful textual syntax for all of the above, which we'll introduce next.
|
||||
- The documentation type provides a rich vocabulary of elements that go beyond markdown, including asides, callouts, tooltips, and more.
|
||||
- Docs may contain Unison code which is parsed and typechecked to ensure validity. No more out of date examples that don't compile or assume a bunch of implicit context\!
|
||||
- Embeded examples are live and can show the results of evaluation. This uses the same evaluation cache as Unison's scratch files, allowing Unison docs to function like well-commented spreadsheets or notebooks.
|
||||
- Links to other definitions are typechecked to ensure they point to valid definitions. The links are resolved to hashes and won't be broken by name changes or moving definitions around.
|
||||
- Docs can be included in other docs and you can assemble documentation programmatically, using Unison code.
|
||||
- There's a powerful textual syntax for all of the above, which we'll introduce next.
|
||||
|
||||
## Introduction
|
||||
|
||||
@ -769,3 +769,4 @@ scratch/main> display doc.guide
|
||||
|
||||
```
|
||||
🌻 THE END
|
||||
|
||||
|
@ -36,6 +36,7 @@ some subtyping.
|
||||
|
||||
However, the ability handling was just processing rows in whatever
|
||||
order they occurred, and during inference it happened that `g`
|
||||
occurred in the row before `Async t g. Processing the stricter parts
|
||||
occurred in the row before `Async t g`. Processing the stricter parts
|
||||
first is better, becauase it can solve things more precisely and avoid
|
||||
ambiguities relating to subtyping.
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
This tests a case where a function was somehow discarding abilities.
|
||||
|
||||
|
||||
``` unison
|
||||
structural ability Trivial where
|
||||
trivial : ()
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Tests a former error due to bad calling conventions on delay.impl
|
||||
|
||||
``` unison
|
||||
|
@ -38,7 +38,7 @@ go = do
|
||||
|
||||
|
||||
```
|
||||
This comes from issue #3513
|
||||
This comes from issue \#3513
|
||||
|
||||
``` unison
|
||||
(<<) : (b ->{e} c) -> (a ->{e} b) -> a ->{e} c
|
||||
|
@ -177,7 +177,7 @@ Note that the universal versions of `hash` and `hmac` are currently unimplemente
|
||||
```
|
||||
## Hashing tests
|
||||
|
||||
Here are some test vectors (taken from [here](https://www.di-mgt.com.au/sha_testvectors.html) and [here](https://en.wikipedia.org/wiki/BLAKE_(hash_function))) for the various hashing algorithms:
|
||||
Here are some test vectors (taken from [here](https://www.di-mgt.com.au/sha_testvectors.html) and [here](https://en.wikipedia.org/wiki/BLAKE_\(hash_function\))) for the various hashing algorithms:
|
||||
|
||||
``` unison
|
||||
ex alg input expected = checks [hashBytes alg (ascii input) == fromHex expected]
|
||||
|
@ -9,7 +9,6 @@ MVars are the building block on which many other concurrency
|
||||
primitives can be built, such as Futures, Run at most once initializer
|
||||
blocks, Queues, etc.
|
||||
|
||||
|
||||
``` unison
|
||||
eitherCk : (a -> Boolean) -> Either e a -> Boolean
|
||||
eitherCk f = cases
|
||||
|
@ -16,14 +16,13 @@ socketAccept = compose reraise socketAccept.impl
|
||||
This section tests functions in the IO builtin related to binding to
|
||||
TCP server socket, as to be able to accept incoming TCP connections.
|
||||
|
||||
```builtin
|
||||
.io2.IO.serverSocket : Optional Text -> Text ->{io2.IO} Either Failure io2.Socket
|
||||
|
||||
```
|
||||
builtin.io2.IO.serverSocket : Optional Text -> Text ->{io2.IO} Either Failure io2.Socket
|
||||
```
|
||||
|
||||
This function takes two parameters, The first is the Hostname. If None
|
||||
is provided, We will attempt to bind to 0.0.0.0 (All ipv4
|
||||
addresses). We currently only support IPV4 (we should fix this!)
|
||||
addresses). We currently only support IPV4 (we should fix this\!)
|
||||
The second is the name of the port to bind to. This can be
|
||||
a decimal representation of a port number between 1-65535. This can be
|
||||
a named port like "ssh" (for port 22) or "kermit" (for port 1649),
|
||||
|
@ -1,5 +1,6 @@
|
||||
Loops that access a shared counter variable, accessed in transactions.
|
||||
Some thread delaying is just accomplished by counting in a loop.
|
||||
|
||||
``` unison
|
||||
count : Nat -> ()
|
||||
count = cases
|
||||
|
@ -54,7 +54,7 @@ scratch/main> add
|
||||
Tip: Use `help filestatus` to learn more.
|
||||
|
||||
```
|
||||
---
|
||||
-----
|
||||
|
||||
``` unison
|
||||
y = 42
|
||||
|
@ -1,4 +1,4 @@
|
||||
Test for new Text -> Bytes conversions explicitly using UTF-8 as the encoding
|
||||
Test for new Text -\> Bytes conversions explicitly using UTF-8 as the encoding
|
||||
|
||||
Unison has function for converting between `Text` and a UTF-8 `Bytes` encoding of the Text.
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Some random ability stuff to ensure things work.
|
||||
|
||||
``` unison
|
||||
|
@ -31,7 +31,7 @@ scratch/main> add
|
||||
ability Channels
|
||||
|
||||
```
|
||||
Now we update the ability, changing the name of the constructor, _but_, we simultaneously
|
||||
Now we update the ability, changing the name of the constructor, *but*, we simultaneously
|
||||
add a new top-level term with the same name as the constructor which is being
|
||||
removed from Channels.
|
||||
|
||||
@ -89,7 +89,7 @@ scratch/main> update.old patch thing
|
||||
ability Channels
|
||||
|
||||
```
|
||||
If however, `Channels.send` and `thing` _depend_ on `Channels`, updating them should succeed since it pulls in the ability as a dependency.
|
||||
If however, `Channels.send` and `thing` *depend* on `Channels`, updating them should succeed since it pulls in the ability as a dependency.
|
||||
|
||||
``` unison
|
||||
unique ability Channels where
|
||||
|
@ -47,6 +47,7 @@ scratch/main> run is2even
|
||||
```
|
||||
it errors if the desired result name conflicts with a name in the
|
||||
unison file
|
||||
|
||||
``` ucm
|
||||
scratch/main> add.run is2even
|
||||
|
||||
@ -57,6 +58,7 @@ scratch/main> add.run is2even
|
||||
|
||||
```
|
||||
otherwise, the result is successfully persisted
|
||||
|
||||
``` ucm
|
||||
scratch/main> add.run foo.bar.baz
|
||||
|
||||
@ -223,6 +225,7 @@ x = 50
|
||||
|
||||
```
|
||||
this saves 2 to xres, rather than 100
|
||||
|
||||
``` ucm
|
||||
scratch/main> add.run xres
|
||||
|
||||
|
@ -3,7 +3,7 @@ test> foo : [Test.Result]
|
||||
foo = []
|
||||
```
|
||||
|
||||
Apparently when we add a test watch, we add a type annotation to it, even if it already has one. We don't want this to happen though!
|
||||
Apparently when we add a test watch, we add a type annotation to it, even if it already has one. We don't want this to happen though\!
|
||||
|
||||
``` ucm
|
||||
scratch/main> add
|
||||
|
@ -1,17 +1,14 @@
|
||||
The `alias.many` command can be used to copy definitions from the current namespace into your curated one.
|
||||
The names that will be used in the target namespace are the names you specify, relative to the current namespace:
|
||||
|
||||
```scratch
|
||||
/main> help alias.many
|
||||
scratch/main> help alias.many
|
||||
|
||||
alias.many (or copy)
|
||||
`alias.many <relative1> [relative2...] <namespace>` creates aliases `relative1`, `relative2`, ...
|
||||
in the namespace `namespace`.
|
||||
`alias.many foo.foo bar.bar .quux` creates aliases `.quux.foo.foo` and `.quux.bar.bar`.
|
||||
|
||||
```
|
||||
|
||||
Let's try it!
|
||||
Let's try it\!
|
||||
|
||||
``` ucm
|
||||
scratch/main> alias.many List.adjacentPairs List.all List.any List.chunk List.chunksOf List.dropWhile List.first List.init List.intersperse List.isEmpty List.last List.replicate List.splitAt List.tail List.takeWhile mylib
|
||||
@ -63,4 +60,5 @@ scratch/main> find-in mylib
|
||||
|
||||
|
||||
```
|
||||
Thanks, `alias.many!
|
||||
Thanks, `alias.many`\!
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
|
||||
This tests a variable related bug in the ANF compiler.
|
||||
|
||||
The nested let would get flattened out, resulting in:
|
||||
|
||||
bar = result
|
||||
|
||||
which would be handled by renaming. However, the _context_ portion of
|
||||
which would be handled by renaming. However, the *context* portion of
|
||||
the rest of the code was not being renamed correctly, so `bar` would
|
||||
remain in the definition of `baz`.
|
||||
|
||||
|
@ -941,3 +941,4 @@ GET /api/projects/scratch/branches/main/getDefinition?names=term
|
||||
"typeDefinitions": {}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -253,3 +253,4 @@ GET /api/projects/scratch/branches/main/find?query=joey.http
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
|
@ -205,7 +205,9 @@ GET /api/projects/scratch/branches/main/getDefinition?names=%23qkhkl0n238&relati
|
||||
},
|
||||
"typeDefinitions": {}
|
||||
}
|
||||
``````unison
|
||||
```
|
||||
|
||||
``` unison
|
||||
doctest.thing.doc = {{ The correct docs for the thing }}
|
||||
doctest.thing = "A thing"
|
||||
doctest.thingalias.doc = {{ Docs for the alias, should not be displayed }}
|
||||
@ -332,7 +334,9 @@ GET /api/projects/scratch/branches/main/getDefinition?names=thing&relativeTo=doc
|
||||
},
|
||||
"typeDefinitions": {}
|
||||
}
|
||||
```If we request a doc, the api should return the source, but also the rendered doc should appear in the 'termDocs' list.
|
||||
```
|
||||
|
||||
If we request a doc, the api should return the source, but also the rendered doc should appear in the 'termDocs' list.
|
||||
|
||||
``` api
|
||||
GET /api/projects/scratch/branches/main/getDefinition?names=thing.doc&relativeTo=doctest
|
||||
@ -508,3 +512,4 @@ GET /api/projects/scratch/branches/main/getDefinition?names=thing.doc&relativeTo
|
||||
"typeDefinitions": {}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -54,3 +54,4 @@ GET /api/projects/project-one/branches?prefix=branch-t
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
@ -79,3 +79,4 @@ GET /api/projects/scratch/branches/main/namespaces/nested.names
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -132,3 +132,4 @@ GET /api/projects/scratch/branches/main/list?namespace=names&relativeTo=nested
|
||||
"namespaceListingHash": "#oms19b4f9s3c8tb5skeb8jii95ij35n3hdg038pu6rv5b0fikqe4gd7lnu6a1i6aq5tdh2opdo4s0sfrupvk6vfkr9lf0n752gbl8o0"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -667,7 +667,9 @@ GET /api/projects/scratch/branches/main/definitions/terms/by-hash/@@IO.putBytes.
|
||||
},
|
||||
"tag": "Plain"
|
||||
}
|
||||
```## Type Summary APIs
|
||||
```
|
||||
|
||||
## Type Summary APIs
|
||||
|
||||
``` api
|
||||
-- data
|
||||
@ -824,3 +826,4 @@ GET /api/projects/scratch/branches/main/definitions/types/by-hash/@@Nat/summary?
|
||||
"tag": "Data"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -103,7 +103,7 @@ ex thing =
|
||||
4201
|
||||
|
||||
```
|
||||
Here's another example, showing that bindings cannot reference bindings declared in blocks nested in the _body_ (the final expression) of a block:
|
||||
Here's another example, showing that bindings cannot reference bindings declared in blocks nested in the *body* (the final expression) of a block:
|
||||
|
||||
``` unison
|
||||
ex thing =
|
||||
@ -137,7 +137,7 @@ ex thing =
|
||||
```
|
||||
### Blocks can define one or more functions which are recursive or mutually recursive
|
||||
|
||||
We call these groups of definitions that reference each other in a block _cycles_. For instance:
|
||||
We call these groups of definitions that reference each other in a block *cycles*. For instance:
|
||||
|
||||
``` unison
|
||||
sumTo n =
|
||||
@ -253,7 +253,7 @@ ex n =
|
||||
ex : n -> r
|
||||
|
||||
```
|
||||
Just don't try to run it as it's an infinite loop!
|
||||
Just don't try to run it as it's an infinite loop\!
|
||||
|
||||
### Cyclic definitions in a block don't have access to any abilities
|
||||
|
||||
@ -279,7 +279,7 @@ ex n =
|
||||
|
||||
|
||||
```
|
||||
### The _body_ of recursive functions can certainly access abilities
|
||||
### The *body* of recursive functions can certainly access abilities
|
||||
|
||||
For instance, this works fine:
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
We can display the guide before and after adding it to the codebase:
|
||||
|
||||
``` ucm
|
||||
|
@ -153,6 +153,7 @@ test> Nat.tests.conversions =
|
||||
```
|
||||
|
||||
## `Boolean` functions
|
||||
|
||||
``` unison
|
||||
test> Boolean.tests.orTable =
|
||||
checks [
|
||||
@ -345,6 +346,7 @@ test> checks [
|
||||
```
|
||||
|
||||
Other list functions
|
||||
|
||||
``` unison
|
||||
test> checks [
|
||||
List.take bigN [1,2,3] == [1,2,3],
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
This should render as `Bytes.fromList [1,2,3,4]`, not `##Bytes.fromSequence [1,2,3,4]`:
|
||||
|
||||
``` unison
|
||||
|
@ -1,6 +1,7 @@
|
||||
First we'll set up two libraries, and then we'll use them in some projects and show what `names` are deep-loaded for them.
|
||||
|
||||
Our two "libraries":
|
||||
|
||||
``` unison
|
||||
text.a = 1
|
||||
text.b = 2
|
||||
@ -12,6 +13,7 @@ http.z = 8
|
||||
```
|
||||
|
||||
Our `app1` project includes the text library twice and the http library twice as direct dependencies.
|
||||
|
||||
``` ucm
|
||||
scratch/app1> fork text lib.text_v1
|
||||
|
||||
@ -39,6 +41,7 @@ scratch/app1> delete.namespace http
|
||||
|
||||
```
|
||||
As such, we see two copies of `a` and two copies of `x` via these direct dependencies.
|
||||
|
||||
``` ucm
|
||||
scratch/app1> names a
|
||||
|
||||
@ -59,6 +62,7 @@ scratch/app1> names x
|
||||
```
|
||||
Our `app2` project includes the `http` library twice as direct dependencies, and once as an indirect dependency via `webutil`.
|
||||
It also includes the `text` library twice as indirect dependencies via `webutil`
|
||||
|
||||
``` ucm
|
||||
scratch/app2> fork http lib.http_v1
|
||||
|
||||
@ -91,6 +95,7 @@ scratch/app2> delete.namespace text
|
||||
```
|
||||
Now we see two copies of `x` via direct dependencies on `http`, and one copy of `a` via indirect dependency on `text` via `webutil`.
|
||||
We see neither the second indirect copy of `a` nor the indirect copy of `x` via webutil because we already have names for them.
|
||||
|
||||
``` ucm
|
||||
scratch/app2> names a
|
||||
|
||||
|
@ -558,7 +558,9 @@ GET /api/projects/diffs/diff/terms?oldBranchRef=main&newBranchRef=new&oldTerm=te
|
||||
},
|
||||
"project": "diffs"
|
||||
}
|
||||
```Diff types
|
||||
```
|
||||
|
||||
Diff types
|
||||
|
||||
``` api
|
||||
GET /api/projects/diffs/diff/types?oldBranchRef=main&newBranchRef=new&oldType=Type&newType=Type
|
||||
@ -805,3 +807,4 @@ GET /api/projects/diffs/diff/types?oldBranchRef=main&newBranchRef=new&oldType=Ty
|
||||
"project": "diffs"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
# Delete namespace dependents check
|
||||
|
||||
This is a regression test, previously `delete.namespace` allowed a delete as long as the deletions had a name _anywhere_ in your codebase, it should only check the current project branch.
|
||||
This is a regression test, previously `delete.namespace` allowed a delete as long as the deletions had a name *anywhere* in your codebase, it should only check the current project branch.
|
||||
|
||||
``` unison
|
||||
sub.dependency = 123
|
||||
|
@ -1,7 +1,9 @@
|
||||
### `debug.file`
|
||||
|
||||
I can use `debug.file` to see the hashes of the last typechecked file.
|
||||
|
||||
Given this .u file:
|
||||
|
||||
``` unison
|
||||
structural type outside.A = A Nat outside.B
|
||||
structural type outside.B = B Int
|
||||
@ -30,7 +32,9 @@ scratch/main> debug.file
|
||||
This will help me make progress in some situations when UCM is being deficient or broken.
|
||||
|
||||
### `dependents` / `dependencies`
|
||||
|
||||
But wait, there's more. I can check the dependencies and dependents of a definition:
|
||||
|
||||
``` ucm
|
||||
scratch/main> add
|
||||
|
||||
@ -110,3 +114,4 @@ scratch/main> dependents d
|
||||
|
||||
```
|
||||
We don't have an index for dependents of constructors, but iirc if you ask for that, it will show you dependents of the structural type that provided the constructor.
|
||||
|
||||
|
@ -52,14 +52,14 @@ scratch/main> diff.namespace /b1: /b2:
|
||||
```
|
||||
Things we want to test:
|
||||
|
||||
* Diffing identical namespaces
|
||||
* Adds, removes, updates
|
||||
* Adds with multiple names
|
||||
* Moved and copied definitions
|
||||
* Moves that have more that 1 initial or final name
|
||||
* ... terms and types
|
||||
* New patches, modified patches, deleted patches, moved patches
|
||||
* With and without propagated updates
|
||||
- Diffing identical namespaces
|
||||
- Adds, removes, updates
|
||||
- Adds with multiple names
|
||||
- Moved and copied definitions
|
||||
- Moves that have more that 1 initial or final name
|
||||
- ... terms and types
|
||||
- New patches, modified patches, deleted patches, moved patches
|
||||
- With and without propagated updates
|
||||
|
||||
``` unison
|
||||
fromJust = 1
|
||||
@ -504,43 +504,44 @@ Updates: -- 1 to 1
|
||||
|
||||
New name conflicts: -- updates where RHS has multiple hashes (excluding when RHS=LHS)
|
||||
|
||||
1. foo#jk19sm5bf8 : Nat - do we want to force a hashqualified? Arya thinks so
|
||||
1. foo\#jk19sm5bf8 : Nat - do we want to force a hashqualified? Arya thinks so
|
||||
↓
|
||||
2. ┌ foo#0ja1qfpej6 : Nat
|
||||
3. └ foo#jk19sm5bf8 : Nat
|
||||
2. ┌ foo\#0ja1qfpej6 : Nat
|
||||
3. └ foo\#jk19sm5bf8 : Nat
|
||||
|
||||
Resolved name conflicts: -- updates where LHS had multiple hashes and RHS has one
|
||||
|
||||
4. ┌ bar#0ja1qfpej6 : Nat
|
||||
5. └ bar#jk19sm5bf8 : Nat
|
||||
4. ┌ bar\#0ja1qfpej6 : Nat
|
||||
5. └ bar\#jk19sm5bf8 : Nat
|
||||
↓
|
||||
6. bar#jk19sm5bf8 : Nat
|
||||
6. bar\#jk19sm5bf8 : Nat
|
||||
|
||||
## Display issues to fixup
|
||||
|
||||
- [d] Do we want to surface new edit conflicts in patches?
|
||||
- [t] two different auto-propagated changes creating a name conflict should show
|
||||
- \[d\] Do we want to surface new edit conflicts in patches?
|
||||
- \[t\] two different auto-propagated changes creating a name conflict should show
|
||||
up somewhere besides the auto-propagate count
|
||||
- [t] Things look screwy when the type signature doesn't fit and has to get broken
|
||||
- \[t\] Things look screwy when the type signature doesn't fit and has to get broken
|
||||
up into multiple lines. Maybe just disallow that?
|
||||
- [d] Delete blank line in between copies / renames entries if all entries are 1 to 1
|
||||
- \[d\] Delete blank line in between copies / renames entries if all entries are 1 to 1
|
||||
see todo in the code
|
||||
- [x] incorrectly calculated bracket alignment on hashqualified "Name changes" (delete.output.md)
|
||||
- [x] just handle deletion of isPropagated in propagate function, leave HandleInput alone (assuming this does the trick)
|
||||
- [x] might want unqualified names to be qualified sometimes:
|
||||
- [x] if a name is updated to a not-yet-named reference, it's shown as both an update and an add
|
||||
- [x] similarly, if a conflicted name is resolved by deleting the last name to
|
||||
- \[x\] incorrectly calculated bracket alignment on hashqualified "Name changes" (delete.output.md)
|
||||
- \[x\] just handle deletion of isPropagated in propagate function, leave HandleInput alone (assuming this does the trick)
|
||||
- \[x\] might want unqualified names to be qualified sometimes:
|
||||
- \[x\] if a name is updated to a not-yet-named reference, it's shown as both an update and an add
|
||||
- \[x\] similarly, if a conflicted name is resolved by deleting the last name to
|
||||
a reference, I (arya) suspect it will show up as a Remove
|
||||
- [d] Maybe group and/or add headings to the types, constructors, terms
|
||||
- [x] add tagging of propagated updates to test propagated updates output
|
||||
- [x] missing old names in deletion ppe (delete.output.md) (superseded by \#1143)
|
||||
- [x] delete.term has some bonkers output
|
||||
- [x] Make a decision about how we want to show constructors in the diff
|
||||
- [x] 12.patch patch needs a space
|
||||
- [x] This looks like garbage
|
||||
- [x] Extra 2 blank lines at the end of the add section
|
||||
- [x] Fix alignment issues with buildTable, convert to column3M (to be written)
|
||||
- [x] adding an alias is showing up as an Add and a Copy; should just show as Copy
|
||||
- [x] removing one of multiple aliases appears in removes + moves + copies section
|
||||
- [x] some overlapping cases between Moves and Copies^
|
||||
- [x] Maybe don't list the type signature twice for aliases?
|
||||
- \[d\] Maybe group and/or add headings to the types, constructors, terms
|
||||
- \[x\] add tagging of propagated updates to test propagated updates output
|
||||
- \[x\] missing old names in deletion ppe (delete.output.md) (superseded by \#1143)
|
||||
- \[x\] delete.term has some bonkers output
|
||||
- \[x\] Make a decision about how we want to show constructors in the diff
|
||||
- \[x\] 12.patch patch needs a space
|
||||
- \[x\] This looks like garbage
|
||||
- \[x\] Extra 2 blank lines at the end of the add section
|
||||
- \[x\] Fix alignment issues with buildTable, convert to column3M (to be written)
|
||||
- \[x\] adding an alias is showing up as an Add and a Copy; should just show as Copy
|
||||
- \[x\] removing one of multiple aliases appears in removes + moves + copies section
|
||||
- \[x\] some overlapping cases between Moves and Copies^
|
||||
- \[x\] Maybe don't list the type signature twice for aliases?
|
||||
|
||||
|
@ -510,6 +510,7 @@ test2 = [:
|
||||
|
||||
```
|
||||
View is fine.
|
||||
|
||||
``` ucm
|
||||
scratch/main> view test2
|
||||
|
||||
@ -521,6 +522,7 @@ scratch/main> view test2
|
||||
|
||||
```
|
||||
But note it's not obvious how display should best be handling this. At the moment it just does the simplest thing:
|
||||
|
||||
``` ucm
|
||||
scratch/main> display test2
|
||||
|
||||
|
@ -43,11 +43,11 @@ Syntax:
|
||||
|
||||
`[:` starts a documentation block; `:]` finishes it. Within the block:
|
||||
|
||||
* Links to definitions are done with `@List`. `\@` (and `\:]`) if you want to escape.
|
||||
* `@[signature] List.take` expands to the type signature of `List.take`
|
||||
* `@[source] List.map` expands to the full source of `List.map`
|
||||
* `@[include] someOtherDoc`, inserts a value `someOtherDoc : Doc` here.
|
||||
* `@[evaluate] someDefinition` expands to the result of evaluating `someDefinition`, which must be a pre-existing definition in the codebase (can't be an arbitrary expression).
|
||||
- Links to definitions are done with `@List`. `\@` (and `\:]`) if you want to escape.
|
||||
- `@[signature] List.take` expands to the type signature of `List.take`
|
||||
- `@[source] List.map` expands to the full source of `List.map`
|
||||
- `@[include] someOtherDoc`, inserts a value `someOtherDoc : Doc` here.
|
||||
- `@[evaluate] someDefinition` expands to the result of evaluating `someDefinition`, which must be a pre-existing definition in the codebase (can't be an arbitrary expression).
|
||||
|
||||
### An example
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
If `foo#old` exists in old, and `foo#new` exists in new, you might think `upgrade old new` would rewrite references to
|
||||
`#old` with references to `#new`. And it will... !!unless!! `#old` still exists in new.
|
||||
`#old` with references to `#new`. And it will... \!\!unless\!\! `#old` still exists in new.
|
||||
|
||||
``` unison
|
||||
lib.old.foo = 18
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
``` unison
|
||||
up = 0xs0123456789abcdef
|
||||
down = 0xsfedcba9876543210
|
||||
|
@ -16,7 +16,6 @@ mytest = [Ok "ok"]
|
||||
|
||||
```
|
||||
|
||||
|
||||
``` ucm
|
||||
|
||||
Loading changes detected in /private/tmp/scratch.u.
|
||||
|
@ -5,6 +5,7 @@ mynamespace.x = 1
|
||||
```
|
||||
|
||||
The deleted namespace shouldn't appear in `ls` output.
|
||||
|
||||
``` ucm
|
||||
scratch/main> ls
|
||||
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
The Unison codebase, when first initialized, contains no definitions in its namespace.
|
||||
|
||||
Not even `Nat` or `+`!
|
||||
Not even `Nat` or `+`\!
|
||||
|
||||
BEHOLD!!!
|
||||
BEHOLD\!\!\!
|
||||
|
||||
``` ucm
|
||||
scratch/main> ls
|
||||
@ -37,4 +37,5 @@ scratch/main> ls lib
|
||||
2. builtinsio/ (643 terms, 92 types)
|
||||
|
||||
```
|
||||
More typically, you'd start out by pulling `base.
|
||||
More typically, you'd start out by pulling `base`.
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
This file contains programs with parse errors and type errors, for visual inspection of error message quality and to check for regressions or changes to error reporting.
|
||||
|
||||
## Parse errors
|
||||
@ -212,7 +211,8 @@ foo = match 1 with
|
||||
|
||||
I got confused here:
|
||||
|
||||
3 |
|
||||
2 | 2 -- no right-hand-side
|
||||
|
||||
|
||||
I was surprised to find an end of section here.
|
||||
I was expecting one of these instead:
|
||||
@ -258,7 +258,8 @@ x = match Some a with
|
||||
|
||||
I got confused here:
|
||||
|
||||
7 |
|
||||
6 | 2
|
||||
|
||||
|
||||
I was surprised to find an end of section here.
|
||||
I was expecting one of these instead:
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
### Transcript parser hidden errors
|
||||
|
||||
When an error is encountered in a `unison:hide:all` block
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
### Transcript parser hidden errors
|
||||
|
||||
When an error is encountered in a `unison:hide:all` block
|
||||
|
@ -1,7 +1,6 @@
|
||||
|
||||
### Transcript parser hidden errors
|
||||
|
||||
Dangerous scary words!
|
||||
Dangerous scary words\!
|
||||
|
||||
When an expected error is not encountered in a `ucm:hide:all` block
|
||||
then the transcript parser should print the stanza
|
||||
@ -12,6 +11,7 @@ scratch/main> history
|
||||
```
|
||||
|
||||
|
||||
|
||||
🛑
|
||||
|
||||
The transcript was expecting an error in the stanza above, but did not encounter one.
|
||||
|
@ -1,7 +1,6 @@
|
||||
|
||||
### Transcript parser hidden errors
|
||||
|
||||
Dangerous scary words!
|
||||
Dangerous scary words\!
|
||||
|
||||
When an error is encountered in a `ucm:hide:all` block
|
||||
then the transcript parser should print the stanza
|
||||
@ -12,6 +11,7 @@ scratch/main> move.namespace foo bar
|
||||
```
|
||||
|
||||
|
||||
|
||||
🛑
|
||||
|
||||
The transcript failed due to an error in the stanza above. The error is:
|
||||
|
@ -1,7 +1,6 @@
|
||||
|
||||
### Transcript parser hidden errors
|
||||
|
||||
Dangerous scary words!
|
||||
Dangerous scary words\!
|
||||
|
||||
When an expected error is not encountered in a `ucm:hide` block
|
||||
then the transcript parser should print the stanza
|
||||
@ -12,6 +11,7 @@ scratch/main> history
|
||||
```
|
||||
|
||||
|
||||
|
||||
🛑
|
||||
|
||||
The transcript was expecting an error in the stanza above, but did not encounter one.
|
||||
|
@ -1,7 +1,6 @@
|
||||
|
||||
### Transcript parser hidden errors
|
||||
|
||||
Dangerous scary words!
|
||||
Dangerous scary words\!
|
||||
|
||||
When an error is encountered in a `ucm:hide` block
|
||||
then the transcript parser should print the stanza
|
||||
@ -12,6 +11,7 @@ scratch/main> move.namespace foo bar
|
||||
```
|
||||
|
||||
|
||||
|
||||
🛑
|
||||
|
||||
The transcript failed due to an error in the stanza above. The error is:
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
### Transcript parser hidden errors
|
||||
|
||||
When an expected error is not encountered in a `unison:hide:all:error` block
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
### Transcript parser hidden errors
|
||||
|
||||
When an error is encountered in a `unison:hide:all` block
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
### Transcript parser hidden errors
|
||||
|
||||
When an expected error is not encountered in a `unison:hide:error` block
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
### Transcript parser hidden errors
|
||||
|
||||
When an error is encountered in a `unison:hide` block
|
||||
|
@ -1,6 +1,7 @@
|
||||
We were seeing an issue where (it seemed) that every namespace that was visited during a propagate would get a new history node, even when it didn't contain any dependents.
|
||||
|
||||
Example:
|
||||
|
||||
``` unison
|
||||
a = "a term"
|
||||
X.foo = "a namespace"
|
||||
@ -16,6 +17,7 @@ scratch/main> add
|
||||
|
||||
```
|
||||
Here is an update which should not affect `X`:
|
||||
|
||||
``` unison
|
||||
a = "an update"
|
||||
```
|
||||
@ -30,6 +32,7 @@ scratch/main> update
|
||||
|
||||
```
|
||||
As of the time of this writing, the history for `X` should be a single node, `#4eeuo5bsfr`;
|
||||
|
||||
``` ucm
|
||||
scratch/main> history X
|
||||
|
||||
@ -42,6 +45,7 @@ scratch/main> history X
|
||||
|
||||
```
|
||||
however, as of release/M1i, we saw an extraneous node appear. If your `ucm` is fixed, you won't see it below:
|
||||
|
||||
``` ucm
|
||||
scratch/main> history #7nl6ppokhg
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
Previously, the `alias.term` and `alias.type` would fail if the source argument was hash-only, and there was no way to create an alias for a definition that didn't already have a name. Also, the `replace.term` and `replace.type` _only_ worked on hashes, and they had to be _full_ hashes.
|
||||
Previously, the `alias.term` and `alias.type` would fail if the source argument was hash-only, and there was no way to create an alias for a definition that didn't already have a name. Also, the `replace.term` and `replace.type` *only* worked on hashes, and they had to be *full* hashes.
|
||||
|
||||
With this PR, the source of an alias can be a short hash (even of a definition that doesn't currently have a name in the namespace) along with a name or hash-qualified name from the current namespace as usual.
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
``` ucm
|
||||
scratch/main> builtins.merge
|
||||
|
||||
|
8
unison-src/transcripts/fix1421.md
Normal file
8
unison-src/transcripts/fix1421.md
Normal file
@ -0,0 +1,8 @@
|
||||
```ucm
|
||||
scratch/main> alias.type ##Nat Nat
|
||||
scratch/main> alias.term ##Nat.+ Nat.+
|
||||
```
|
||||
```unison
|
||||
unique type A = A Nat
|
||||
unique type B = B Nat Nat
|
||||
```
|
29
unison-src/transcripts/fix1421.output.md
Normal file
29
unison-src/transcripts/fix1421.output.md
Normal file
@ -0,0 +1,29 @@
|
||||
``` ucm
|
||||
scratch/main> alias.type ##Nat Nat
|
||||
|
||||
Done.
|
||||
|
||||
scratch/main> alias.term ##Nat.+ Nat.+
|
||||
|
||||
Done.
|
||||
|
||||
```
|
||||
``` unison
|
||||
unique type A = A Nat
|
||||
unique type B = B Nat Nat
|
||||
```
|
||||
|
||||
``` ucm
|
||||
|
||||
Loading changes detected in scratch.u.
|
||||
|
||||
I found and typechecked these definitions in scratch.u. If you
|
||||
do an `add` or `update`, here's how your codebase would
|
||||
change:
|
||||
|
||||
⍟ These new definitions are ok to `add`:
|
||||
|
||||
type A
|
||||
type B
|
||||
|
||||
```
|
@ -13,9 +13,9 @@ foo.bar = 23
|
||||
|
||||
Suffix-based name resolution prefers to use names locally defined in the current file, then checks for matches in the codebase. Here are the precise rules, which will be explained below with examples:
|
||||
|
||||
* If a symbol, `s`, is a suffix of exactly one definition `d` in the file, then `s` refers to `d`.
|
||||
* Otherwise, if `s` is a suffix of exactly one definition `d` in the codebase, then `s` refers to `d`.
|
||||
* Otherwise, if `s` is a suffix of multiple definitions in the file or the codebase, then (at least for terms) type-directed name resolution will be attempted to figure out which definition `s` refers to.
|
||||
- If a symbol, `s`, is a suffix of exactly one definition `d` in the file, then `s` refers to `d`.
|
||||
- Otherwise, if `s` is a suffix of exactly one definition `d` in the codebase, then `s` refers to `d`.
|
||||
- Otherwise, if `s` is a suffix of multiple definitions in the file or the codebase, then (at least for terms) type-directed name resolution will be attempted to figure out which definition `s` refers to.
|
||||
|
||||
## Example 1: local file term definitions shadow codebase term definitions
|
||||
|
||||
@ -32,7 +32,7 @@ baz = bar ++ ", world!"
|
||||
|
||||
## Example 2: any locally unique term suffix shadows codebase term definitions
|
||||
|
||||
This should also typecheck, using the file's `oog.bar`. This shows you can refer to a definition in the file by any suffix that is unique to definitions in the file (even if that suffix may match other definitions in the _codebase_). See example 4 below for overriding this behavior.
|
||||
This should also typecheck, using the file's `oog.bar`. This shows you can refer to a definition in the file by any suffix that is unique to definitions in the file (even if that suffix may match other definitions in the *codebase*). See example 4 below for overriding this behavior.
|
||||
|
||||
``` unison
|
||||
use Text ++
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
``` unison
|
||||
structural ability Ask where ask : Nat
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
``` unison
|
||||
structural ability CLI where
|
||||
print : Text ->{CLI} ()
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
``` unison
|
||||
printLine : Text ->{IO} ()
|
||||
printLine msg =
|
||||
@ -18,8 +17,8 @@ main3 _ = printLine "🦄 ☁️ 🌈"
|
||||
|
||||
Testing a few variations here:
|
||||
|
||||
* Should be able to run annotated and unannotated main functions in the current file.
|
||||
* Should be able to run annotated and unannotated main functions from the codebase.
|
||||
- Should be able to run annotated and unannotated main functions in the current file.
|
||||
- Should be able to run annotated and unannotated main functions from the codebase.
|
||||
|
||||
``` ucm
|
||||
scratch/main> run main1
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
``` unison
|
||||
structural type One a = One a
|
||||
unique type Woot a b c = Woot a b c
|
||||
|
@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
``` unison
|
||||
structural ability Exception where raise : Failure -> x
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Tests for a case where bad eta reduction was causing erroneous watch
|
||||
output/caching.
|
||||
|
||||
|
@ -37,3 +37,4 @@ fail because the type was invalid.
|
||||
|
||||
The fix was to avoid dropping certain existential variables out of
|
||||
scope.
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
This should not typecheck - the inline `@eval` expression uses abilities.
|
||||
|
||||
``` unison
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
This transcript checks that updates to data types propagate successfully to dependent types and dependent terms that do pattern matching. First let's create some types and terms:
|
||||
|
||||
``` unison
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Tests an issue where pattern matching matrices involving built-in
|
||||
types was discarding default cases in some branches.
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Checks a corner case with type checking involving destructuring binds.
|
||||
|
||||
The binds were causing some sequences of lets to be unnecessarily
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
This tests an issue where ability variables were being defaulted over
|
||||
eagerly. In general, we want to avoid collecting up variables from the
|
||||
use of definitions with types like:
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Tests that delaying an un-annotated higher-rank type gives a normal
|
||||
type error, rather than an internal compiler error.
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Tests for a loop that was previously occurring in the type checker.
|
||||
|
||||
``` unison
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Tests for an ability failure that was caused by order dependence of
|
||||
checking wanted vs. provided abilities. It was necessary to re-check
|
||||
rows until a fixed point is reached.
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Tests an issue with a lack of generality of handlers.
|
||||
|
||||
In general, a set of cases:
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Tests an issue with a lack of generality of handlers.
|
||||
|
||||
In general, a set of cases:
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Tests a variable capture problem.
|
||||
|
||||
After pattern compilation, the match would end up:
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Tests a variable capture problem.
|
||||
|
||||
After pattern compilation, the match would end up:
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
``` unison
|
||||
loop : List Nat -> Nat -> List Nat
|
||||
loop l = cases
|
||||
|
23
unison-src/transcripts/fix2826.md
Normal file
23
unison-src/transcripts/fix2826.md
Normal file
@ -0,0 +1,23 @@
|
||||
```ucm
|
||||
scratch/main> builtins.mergeio
|
||||
```
|
||||
|
||||
Supports fences that are longer than three backticks.
|
||||
|
||||
````unison
|
||||
|
||||
doc = {{
|
||||
@typecheck ```
|
||||
x = 3
|
||||
```
|
||||
}}
|
||||
|
||||
````
|
||||
|
||||
And round-trips properly.
|
||||
|
||||
```ucm
|
||||
scratch/main> add
|
||||
scratch/main> edit doc
|
||||
scratch/main> load scratch.u
|
||||
```
|
66
unison-src/transcripts/fix2826.output.md
Normal file
66
unison-src/transcripts/fix2826.output.md
Normal file
@ -0,0 +1,66 @@
|
||||
``` ucm
|
||||
scratch/main> builtins.mergeio
|
||||
|
||||
Done.
|
||||
|
||||
```
|
||||
Supports fences that are longer than three backticks.
|
||||
|
||||
```` unison
|
||||
doc = {{
|
||||
@typecheck ```
|
||||
x = 3
|
||||
```
|
||||
}}
|
||||
|
||||
````
|
||||
|
||||
``` ucm
|
||||
|
||||
Loading changes detected in scratch.u.
|
||||
|
||||
I found and typechecked these definitions in scratch.u. If you
|
||||
do an `add` or `update`, here's how your codebase would
|
||||
change:
|
||||
|
||||
⍟ These new definitions are ok to `add`:
|
||||
|
||||
doc : Doc2
|
||||
|
||||
```
|
||||
And round-trips properly.
|
||||
|
||||
``` ucm
|
||||
scratch/main> add
|
||||
|
||||
⍟ I've added these definitions:
|
||||
|
||||
doc : Doc2
|
||||
|
||||
scratch/main> edit doc
|
||||
|
||||
☝️
|
||||
|
||||
I added 1 definitions to the top of scratch.u
|
||||
|
||||
You can edit them there, then run `update` to replace the
|
||||
definitions currently in this namespace.
|
||||
|
||||
scratch/main> load scratch.u
|
||||
|
||||
Loading changes detected in scratch.u.
|
||||
|
||||
I found and typechecked the definitions in scratch.u. This
|
||||
file has been previously added to the codebase.
|
||||
|
||||
```
|
||||
```` unison:added-by-ucm scratch.u
|
||||
doc : Doc2
|
||||
doc =
|
||||
{{
|
||||
@typecheck ```
|
||||
x = 3
|
||||
```
|
||||
}}
|
||||
````
|
||||
|
@ -1,6 +1,6 @@
|
||||
This bugfix addresses an issue where embedded Unison code in UCM was expected to be present in the active codebase when the `display` command was used render `Doc` values.
|
||||
|
||||
First, a few \[hidden] definitions necessary for typechecking a simple Doc2.
|
||||
First, a few \[hidden\] definitions necessary for typechecking a simple Doc2.
|
||||
|
||||
``` ucm
|
||||
scratch/main> add
|
||||
@ -18,6 +18,7 @@ scratch/main> add
|
||||
|
||||
```
|
||||
Next, define and display a simple Doc:
|
||||
|
||||
``` unison
|
||||
README = {{
|
||||
Hi
|
||||
@ -32,10 +33,8 @@ scratch/main> display README
|
||||
```
|
||||
Previously, the error was:
|
||||
|
||||
```
|
||||
⚙️ Processing stanza 5 of 7.ucm: PE [("die",SrcLoc {srcLocPackage = "unison-parser-typechecker-0.0.0-He2Hp1llokT2nN4MnUfUXz", srcLocModule = "Unison.Runtime.Interface", srcLocFile = "src/Unison/Runtime/Interface.hs", srcLocStartLine = 118, srcLocStartCol = 18, srcLocEndLine = 118, srcLocEndCol = 60})] Lit
|
||||
AnnotatedText (fromList [Segment {segment = "Unknown term reference: #4522d", annotation = Nothing}])
|
||||
|
||||
```
|
||||
|
||||
but as of this PR, it's okay.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
Also fixes #1519 (it's the same issue).
|
||||
Also fixes \#1519 (it's the same issue).
|
||||
|
||||
``` ucm
|
||||
scratch/main> builtins.merge
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
Tests ability checking in scenarios where one side is concrete and the other is
|
||||
a variable. This was supposed to be covered, but the method wasn't actually
|
||||
symmetric, so doing `equate l r` might work, but not `equate r l`.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user