diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 642e268d4f..69cfe6d1f3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -15,7 +15,7 @@ env: ## Some version numbers that are used during CI ormolu_version: 0.7.2.0 jit_version: "@unison/internal/releases/0.0.25" - runtime_tests_version: "@unison/runtime-tests/releases/0.0.3" + runtime_tests_version: "@unison/runtime-tests/@sellout/unison-5661" ## Some cached directories # a temp path for caching a built `ucm` diff --git a/lib/unison-pretty-printer/src/Unison/PrettyTerminal.hs b/lib/unison-pretty-printer/src/Unison/PrettyTerminal.hs index e79e895727..00ba5fc1ab 100644 --- a/lib/unison-pretty-printer/src/Unison/PrettyTerminal.hs +++ b/lib/unison-pretty-printer/src/Unison/PrettyTerminal.hs @@ -43,9 +43,9 @@ putPretty' p = do width <- getAvailableWidth putStr $ P.toANSI width p +-- | Returns a `P.Width` in the range 80–100, depending on the terminal width. getAvailableWidth :: IO P.Width -getAvailableWidth = - maybe 80 (\s -> 100 `min` P.Width (Terminal.width s)) <$> Terminal.size +getAvailableWidth = maybe 80 (P.Width . min 100 . Terminal.width) <$> Terminal.size putPrettyNonempty :: P.Pretty P.ColorText -> IO () putPrettyNonempty msg = do diff --git a/lib/unison-pretty-printer/src/Unison/Util/Pretty.hs b/lib/unison-pretty-printer/src/Unison/Util/Pretty.hs index 9c5d1f8d08..bf50f5b468 100644 --- a/lib/unison-pretty-printer/src/Unison/Util/Pretty.hs +++ b/lib/unison-pretty-printer/src/Unison/Util/Pretty.hs @@ -90,7 +90,6 @@ module Unison.Util.Pretty parenthesizeCommas, parenthesizeIf, render, - renderUnbroken, rightPad, sep, sepNonEmpty, @@ -112,10 +111,8 @@ module Unison.Util.Pretty table, text, toANSI, - toAnsiUnbroken, toHTML, toPlain, - toPlainUnbroken, underline, withSyntax, wrap, @@ -282,8 +279,7 @@ wrapPreserveSpaces p = wrapImplPreserveSpaces (toLeaves [p]) Wrap _ -> hd : toLeaves tl Append hds -> toLeaves (toList hds ++ tl) --- Cut a list every time a predicate changes. Produces a list of --- non-empty lists. +-- | Cut a list every time a predicate changes. Produces a list of non-empty lists. alternations :: (LL.ListLike s c) => (c -> Bool) -> s -> [s] alternations p s = reverse $ go True s [] where @@ -301,31 +297,26 @@ group :: Pretty s -> Pretty s group p = Pretty (delta p) (Group p) toANSI :: Width -> Pretty CT.ColorText -> String -toANSI avail p = CT.toANSI (render avail p) - -toAnsiUnbroken :: Pretty ColorText -> String -toAnsiUnbroken p = CT.toANSI (renderUnbroken p) +toANSI avail = CT.toANSI . render avail toPlain :: Width -> Pretty CT.ColorText -> String -toPlain avail p = CT.toPlain (render avail p) - -toHTML :: String -> Width -> Pretty CT.ColorText -> String -toHTML cssPrefix avail p = CT.toHTML cssPrefix (render avail p) +toPlain avail = CT.toPlain . render avail -toPlainUnbroken :: Pretty ColorText -> String -toPlainUnbroken p = CT.toPlain (renderUnbroken p) +toHTML :: String -> Pretty CT.ColorText -> String +toHTML cssPrefix = CT.toHTML cssPrefix . render 0 syntaxToColor :: Pretty (ST.SyntaxText' r) -> Pretty ColorText syntaxToColor = fmap $ annotateMaybe . fmap CT.defaultColors --- set the syntax, overriding any present syntax +-- | set the syntax, overriding any present syntax withSyntax :: ST.Element r -> Pretty (ST.SyntaxText' r) -> Pretty (ST.SyntaxText' r) withSyntax e = fmap $ ST.syntax e -renderUnbroken :: (Monoid s, IsString s) => Pretty s -> s -renderUnbroken = render maxBound - +-- | Renders to the underlying literal type. +-- +-- Since `Width` less than @1@ is invalid, those values result in rendering without introducing any automatic line +-- breaks. render :: (Monoid s, IsString s) => Width -> Pretty s -> s render availableWidth p = go mempty [Right p] where @@ -355,7 +346,9 @@ render availableWidth p = go mempty [Right p] Wrap ps -> foldMap flow ps fits p cur = - maxCol (surgery cur <> delta p) < availableWidth + if availableWidth <= 0 + then True + else maxCol (surgery cur <> delta p) < availableWidth where -- Surgically modify 'cur' to pretend it has not exceeded availableWidth. -- This is necessary because sometimes things cannot be split and *must* @@ -1041,7 +1034,7 @@ plural f p = case length f of 1 -> p -- todo: consider use of plural package _ -> - p <> case reverse (toPlainUnbroken p) of + p <> case reverse (toPlain 0 p) of 's' : _ -> "es" _ -> "s" diff --git a/parser-typechecker/src/Unison/KindInference/Solve.hs b/parser-typechecker/src/Unison/KindInference/Solve.hs index 623152972a..162553adc4 100644 --- a/parser-typechecker/src/Unison/KindInference/Solve.hs +++ b/parser-typechecker/src/Unison/KindInference/Solve.hs @@ -460,4 +460,4 @@ prettyUVar :: (Var v) => PrettyPrintEnv -> UVar v loc -> P.Pretty P.ColorText prettyUVar ppe (UVar s t) = TP.pretty ppe t <> " :: " <> P.prettyVar s tracePretty :: P.Pretty P.ColorText -> a -> a -tracePretty p = trace (P.toAnsiUnbroken p) +tracePretty p = trace (P.toANSI 0 p) diff --git a/parser-typechecker/src/Unison/PatternMatchCoverage.hs b/parser-typechecker/src/Unison/PatternMatchCoverage.hs index 75cd0a7ce4..e00adb3ca4 100644 --- a/parser-typechecker/src/Unison/PatternMatchCoverage.hs +++ b/parser-typechecker/src/Unison/PatternMatchCoverage.hs @@ -82,5 +82,5 @@ checkMatch scrutineeType cases = do where title = P.bold doDebug out = case shouldDebug PatternCoverage of - True -> trace (P.toAnsiUnbroken out) + True -> trace (P.toANSI 0 out) False -> id diff --git a/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs b/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs index 8986f4c409..c72f6904b0 100644 --- a/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs +++ b/parser-typechecker/src/Unison/PatternMatchCoverage/Solve.hs @@ -650,7 +650,7 @@ addConstraint con0 nc = do P.hang (P.green "resulting constraint: ") (maybe "contradiction" (prettyNormalizedConstraints ppe) x), "" ] - in if shouldDebug PatternCoverageConstraintSolver then trace (P.toAnsiUnbroken debugOutput) x else x + in if shouldDebug PatternCoverageConstraintSolver then trace (P.toANSI 0 debugOutput) x else x -- | Like 'addConstraint', but for a list of constraints addConstraints :: diff --git a/parser-typechecker/src/Unison/PrettyPrintEnv.hs b/parser-typechecker/src/Unison/PrettyPrintEnv.hs index 005bce8472..cc4508eba6 100644 --- a/parser-typechecker/src/Unison/PrettyPrintEnv.hs +++ b/parser-typechecker/src/Unison/PrettyPrintEnv.hs @@ -148,6 +148,12 @@ patternName env r = empty :: PrettyPrintEnv empty = PrettyPrintEnv mempty mempty +instance Semigroup PrettyPrintEnv where + (<>) = union + +instance Monoid PrettyPrintEnv where + mempty = empty + -- | Prefer names which share a common prefix with any provided target. -- -- Results are sorted according to the longest common prefix found against ANY target. diff --git a/parser-typechecker/src/Unison/PrintError.hs b/parser-typechecker/src/Unison/PrintError.hs index 3604c2afd3..6694372ed9 100644 --- a/parser-typechecker/src/Unison/PrintError.hs +++ b/parser-typechecker/src/Unison/PrintError.hs @@ -1127,15 +1127,18 @@ renderContext env ctx@(C.Context es) = renderTerm :: (IsString s, Var v) => Env -> Term.Term' (TypeVar.TypeVar loc0 v) v loc1 -> s renderTerm env e = - fromString (Color.toPlain $ TermPrinter.pretty' (Just 80) env (TypeVar.lowerTerm e)) + fromString (Color.toPlain $ TermPrinter.pretty' 80 env (TypeVar.lowerTerm e)) renderPattern :: Env -> Pattern ann -> ColorText -renderPattern env e = Pr.renderUnbroken . Pr.syntaxToColor . fst $ TermPrinter.prettyPattern env TermPrinter.emptyAc Precedence.Annotation ([] :: [Symbol]) e +renderPattern env = + Pr.render 0 + . Pr.syntaxToColor + . fst + . TermPrinter.prettyPattern env TermPrinter.emptyAc Precedence.Annotation ([] :: [Symbol]) -- | renders a type with no special styling renderType' :: (IsString s, Var v) => Env -> Type v loc -> s -renderType' env typ = - fromString . Pr.toPlain defaultWidth $ renderType env (const id) typ +renderType' env = fromString . Pr.toPlain defaultWidth . renderType env (const id) -- | `f` may do some styling based on `loc`. -- | You can pass `(const id)` if no styling is needed, or call `renderType'`. @@ -1278,7 +1281,7 @@ renderNoteAsANSI :: String -> Note v a -> String -renderNoteAsANSI w e s n = Pr.toANSI w $ printNoteWithSource e s n +renderNoteAsANSI w e s = Pr.toANSI w . printNoteWithSource e s renderParseErrorAsANSI :: (Var v) => Pr.Width -> String -> Parser.Err v -> String renderParseErrorAsANSI w src = Pr.toANSI w . prettyParseError src diff --git a/parser-typechecker/src/Unison/Syntax/TermPrinter.hs b/parser-typechecker/src/Unison/Syntax/TermPrinter.hs index b13e9bffe2..30ed09a670 100644 --- a/parser-typechecker/src/Unison/Syntax/TermPrinter.hs +++ b/parser-typechecker/src/Unison/Syntax/TermPrinter.hs @@ -72,7 +72,7 @@ import Unison.Var qualified as Var type SyntaxText = S.SyntaxText' Reference --- Gets rid of unsightly "_eta" expansion in the pretty-printed output +-- | Gets rid of unsightly "_eta" expansion in the pretty-printed output etaReduce :: (Var v) => Term3 v a -> Term3 v a etaReduce (LamNamed' v (App' f (Var' v'))) | v == v' && Var.name v == "_eta" = f etaReduce tm = tm @@ -81,8 +81,7 @@ goPretty :: (Var v) => PrettyPrintEnv -> Term2 v at ap v a -> Pretty SyntaxText goPretty ppe tm = runPretty (avoidShadowing tm ppe) $ pretty0 emptyAc $ printAnnotate ppe tm pretty :: (HasCallStack, Var v) => PrettyPrintEnv -> Term v a -> Pretty ColorText -pretty ppe tm = - PP.syntaxToColor $ goPretty ppe tm +pretty ppe = PP.syntaxToColor . goPretty ppe prettyBlock :: (Var v) => Bool -> PrettyPrintEnv -> Term v a -> Pretty ColorText prettyBlock elideUnit ppe = PP.syntaxToColor . prettyBlock' elideUnit ppe @@ -91,11 +90,8 @@ prettyBlock' :: (HasCallStack, Var v) => Bool -> PrettyPrintEnv -> Term v a -> P prettyBlock' elideUnit ppe tm = runPretty (avoidShadowing tm ppe) . pretty0 (emptyBlockAc {elideUnit = elideUnit}) $ printAnnotate ppe tm -pretty' :: (HasCallStack, Var v) => Maybe Width -> PrettyPrintEnv -> Term v a -> ColorText -pretty' (Just width) n t = - PP.render width . PP.syntaxToColor $ goPretty n t -pretty' Nothing n t = - PP.renderUnbroken . PP.syntaxToColor $ goPretty n t +pretty' :: (HasCallStack, Var v) => Width -> PrettyPrintEnv -> Term v a -> ColorText +pretty' width n = PP.render width . pretty n -- Information about the context in which a term appears, which affects how the -- term should be rendered. @@ -977,8 +973,7 @@ prettyBinding' :: HQ.HashQualified Name -> Term v a -> ColorText -prettyBinding' ppe width v t = - PP.render width . PP.syntaxToColor $ prettyBinding ppe v t +prettyBinding' ppe width v = PP.render width . PP.syntaxToColor . prettyBinding ppe v prettyBinding0 :: (HasCallStack, MonadPretty v m) => @@ -1918,12 +1913,12 @@ prettyDoc2 ac tm = do bail tm = brace <$> pretty0 ac tm contains :: Char -> Pretty SyntaxText -> Bool contains c p = - PP.toPlainUnbroken (PP.syntaxToColor p) + PP.toPlain 0 (PP.syntaxToColor p) & elem c -- Finds the longest run of a character and return one bigger than that longestRun c s = case filter (\s -> take 2 s == [c, c]) $ - List.group (PP.toPlainUnbroken $ PP.syntaxToColor s) of + List.group (PP.toPlain 0 $ PP.syntaxToColor s) of [] -> 2 x -> 1 + maximum (map length x) oneMore c inner = replicate (longestRun c inner) c @@ -1939,7 +1934,7 @@ prettyDoc2 ac tm = do prettyDs <- intercalateMapM "\n\n" (go (hdr + 1)) ds pure $ PP.lines - [ PP.text (Text.replicate (PP.widthToInt hdr) "#") <> " " <> prettyTitle, + [ PP.string (replicate (PP.widthToInt hdr) '#') <> " " <> prettyTitle, "", PP.indentN (hdr + 1) prettyDs ] diff --git a/parser-typechecker/src/Unison/Syntax/TypePrinter.hs b/parser-typechecker/src/Unison/Syntax/TypePrinter.hs index 90cd52943e..5f0ff477fa 100644 --- a/parser-typechecker/src/Unison/Syntax/TypePrinter.hs +++ b/parser-typechecker/src/Unison/Syntax/TypePrinter.hs @@ -32,7 +32,6 @@ import Unison.Referent (Referent) import Unison.Settings qualified as Settings import Unison.Syntax.NamePrinter (styleHashQualified'') import Unison.Type -import Unison.Util.ColorText (toPlain) import Unison.Util.Pretty (ColorText, Pretty, Width) import Unison.Util.Pretty qualified as PP import Unison.Util.SyntaxText qualified as S @@ -42,16 +41,13 @@ import Unison.Var qualified as Var type SyntaxText = S.SyntaxText' Reference pretty :: (Var v) => PrettyPrintEnv -> Type v a -> Pretty ColorText -pretty ppe t = PP.syntaxToColor $ prettySyntax ppe t +pretty ppe = PP.syntaxToColor . prettySyntax ppe prettySyntax :: (Var v) => PrettyPrintEnv -> Type v a -> Pretty SyntaxText prettySyntax ppe = runPretty ppe . pretty0 Map.empty (-1) -prettyStr :: (Var v) => Maybe Width -> PrettyPrintEnv -> Type v a -> String -prettyStr (Just width) ppe t = - toPlain . PP.render width . PP.syntaxToColor . runPretty ppe $ pretty0 Map.empty (-1) t -prettyStr Nothing ppe t = - toPlain . PP.render maxBound . PP.syntaxToColor . runPretty ppe $ pretty0 Map.empty (-1) t +prettyStr :: (Var v) => Width -> PrettyPrintEnv -> Type v a -> String +prettyStr width ppe = PP.toPlain width . pretty ppe {- Explanation of precedence handling diff --git a/parser-typechecker/src/Unison/Typechecker/Context.hs b/parser-typechecker/src/Unison/Typechecker/Context.hs index 2d325a9c2a..722459f771 100644 --- a/parser-typechecker/src/Unison/Typechecker/Context.hs +++ b/parser-typechecker/src/Unison/Typechecker/Context.hs @@ -67,6 +67,7 @@ import Data.Sequence qualified as Seq import Data.Sequence.NonEmpty (NESeq) import Data.Sequence.NonEmpty qualified as NESeq import Data.Set qualified as Set +import Data.Set.NonEmpty (NESet) import Data.Text qualified as Text import Unison.ABT qualified as ABT import Unison.Blank qualified as B @@ -105,7 +106,6 @@ import Unison.Typechecker.TypeLookup qualified as TL import Unison.Typechecker.TypeVar qualified as TypeVar import Unison.Var (Var) import Unison.Var qualified as Var -import Data.Set.NonEmpty (NESet) type TypeVar v loc = TypeVar.TypeVar (B.Blank loc) v @@ -390,12 +390,11 @@ substituteSolved :: [Element v loc] -> InfoNote v loc -> InfoNote v loc -substituteSolved ctx = \case +substituteSolved ctx = \case (SolvedBlank b v t) -> SolvedBlank b v (applyCtx ctx t) VarBinding v loc t -> VarBinding v loc (applyCtx ctx t) i -> i - -- The typechecker generates synthetic type variables as part of type inference. -- This function converts these synthetic type variables to regular named type -- variables guaranteed to not collide with any other type variables. @@ -620,7 +619,7 @@ debugTrace e | debugEnabled = trace e False debugTrace _ = False showType :: (Var v) => Type.Type v a -> String -showType ty = TP.prettyStr (Just 120) PPE.empty ty +showType ty = TP.prettyStr 120 PPE.empty ty debugType :: (Var v) => String -> Type.Type v a -> Bool debugType tag ty @@ -1120,7 +1119,7 @@ noteTopLevelType e binding typ = case binding of -- | Take note of the types and locations of all bindings, including let bindings, letrec -- bindings, lambda argument bindings and top-level bindings. -- This information is used to provide information to the LSP after typechecking. -noteVarBinding :: (Var v) => v -> loc -> Type v loc -> M v loc () +noteVarBinding :: (Var v) => v -> loc -> Type v loc -> M v loc () noteVarBinding v loc t = btw $ VarBinding v loc t noteVarMention :: (Var v) => v -> loc -> M v loc () @@ -1244,7 +1243,7 @@ synthesizeWanted (Term.Let1Top' top binding boundVarAnn e) = do appendContext [Ann v' boundVarAnn tbinding] (t, w) <- synthesize (ABT.bindInheritAnnotation e (Term.var () v')) t <- applyM t - when top $ noteTopLevelType e binding tbinding + when top $ noteTopLevelType e binding tbinding want <- coalesceWanted w wb -- doRetract $ Ann v' tbinding pure (t, want) @@ -1328,7 +1327,7 @@ synthesizeWanted e synthesizeApps e ft v -- ->I=> (Full Damas Milner rule) - -- | Term.Lam' body <- e = do + -- \| Term.Lam' body <- e = do | (ABT.Tm' (Term.Lam (ABT.Abs' boundVarAnn body))) <- e = do -- arya: are there more meaningful locations we could put into and -- pull out of the abschain?) @@ -1879,7 +1878,8 @@ annotateLetRecBindings isTop letrec = btw $ topLevelComponent ((\(v, b) -> (Var.reset v, b, False)) . unTypeVar <$> vts) pure body - else do -- If this isn't a top-level letrec, then we don't have to do anything special + else do + -- If this isn't a top-level letrec, then we don't have to do anything special (body, _vts) <- annotateLetRecBindings' True pure body where @@ -2479,7 +2479,7 @@ checkWanted want (Term.LetRecNamed' [] m) t = -- letrec can't have effects, so it doesn't extend the wanted set checkWanted want (Term.LetRecAnnotatedTop' isTop lr) t = markThenRetractWanted (Var.named "let-rec-marker") $ do - e <- annotateLetRecBindings isTop lr + e <- annotateLetRecBindings isTop lr checkWanted want e t checkWanted want e@(Term.Match' scrut cases) t = do (scrutType, swant) <- synthesize scrut @@ -3403,11 +3403,11 @@ instance (Var v) => Show (Element v loc) where show (Var v) = case v of TypeVar.Universal x -> "@" <> show x e -> show e - show (Solved _ v t) = "'" ++ Text.unpack (Var.name v) ++ " = " ++ TP.prettyStr Nothing PPE.empty (Type.getPolytype t) + show (Solved _ v t) = "'" ++ Text.unpack (Var.name v) ++ " = " ++ TP.prettyStr 0 PPE.empty (Type.getPolytype t) show (Ann v _loc t) = Text.unpack (Var.name v) ++ " : " - ++ TP.prettyStr Nothing PPE.empty t + ++ TP.prettyStr 0 PPE.empty t show (Marker v) = "|" ++ Text.unpack (Var.name v) ++ "|" instance (Ord loc, Var v) => Show (Context v loc) where @@ -3416,8 +3416,8 @@ instance (Ord loc, Var v) => Show (Context v loc) where showElem _ctx (Var v) = case v of TypeVar.Universal x -> "@" <> show x e -> show e - showElem ctx (Solved _ v (Type.Monotype t)) = "'" ++ Text.unpack (Var.name v) ++ " = " ++ TP.prettyStr Nothing PPE.empty (apply ctx t) - showElem ctx (Ann v _loc t) = Text.unpack (Var.name v) ++ " : " ++ TP.prettyStr Nothing PPE.empty (apply ctx t) + showElem ctx (Solved _ v (Type.Monotype t)) = "'" ++ Text.unpack (Var.name v) ++ " = " ++ TP.prettyStr 0 PPE.empty (apply ctx t) + showElem ctx (Ann v _loc t) = Text.unpack (Var.name v) ++ " : " ++ TP.prettyStr 0 PPE.empty (apply ctx t) showElem _ (Marker v) = "|" ++ Text.unpack (Var.name v) ++ "|" instance (Monad f) => Monad (MT v loc f) where diff --git a/parser-typechecker/tests/Unison/Test/Syntax/TypePrinter.hs b/parser-typechecker/tests/Unison/Test/Syntax/TypePrinter.hs index 69a09a6874..269788f411 100644 --- a/parser-typechecker/tests/Unison/Test/Syntax/TypePrinter.hs +++ b/parser-typechecker/tests/Unison/Test/Syntax/TypePrinter.hs @@ -23,10 +23,7 @@ tc_diff_rtt rtt s expected width = let input_type = Common.t s get_names = PPE.makePPE (PPE.hqNamer Common.hqLength Unison.Builtin.names) PPE.dontSuffixify prettied = fmap toPlain $ PP.syntaxToColor . runPretty get_names $ prettyRaw Map.empty (-1) input_type - actual = - if width == 0 - then PP.renderUnbroken $ prettied - else PP.render width $ prettied + actual = PP.render width prettied actual_reparsed = Common.t actual in scope s $ tests diff --git a/unison-cli/src/Unison/Codebase/Editor/HandleInput/DebugSynhashTerm.hs b/unison-cli/src/Unison/Codebase/Editor/HandleInput/DebugSynhashTerm.hs index 42944c2dac..401bae0ef9 100644 --- a/unison-cli/src/Unison/Codebase/Editor/HandleInput/DebugSynhashTerm.hs +++ b/unison-cli/src/Unison/Codebase/Editor/HandleInput/DebugSynhashTerm.hs @@ -50,7 +50,7 @@ handleDebugSynhashTerm name = do tokens & map prettyToken & Pretty.lines - & Pretty.toAnsiUnbroken + & Pretty.toANSI 0 & Text.pack liftIO (Text.writeFile (Text.unpack filename) renderedTokens) Cli.respond (Output'DebugSynhashTerm ref (Hashable.accumulate tokens) filename) diff --git a/unison-cli/src/Unison/CommandLine/Completion.hs b/unison-cli/src/Unison/CommandLine/Completion.hs index 469d7dec32..ba05040ef1 100644 --- a/unison-cli/src/Unison/CommandLine/Completion.hs +++ b/unison-cli/src/Unison/CommandLine/Completion.hs @@ -315,13 +315,13 @@ prettyCompletionWithQueryPrefix :: Line.Completion prettyCompletionWithQueryPrefix endWithSpace query s = let coloredMatch = P.hiBlack (P.string query) <> P.string (drop (length query) s) - in Line.Completion s (P.toAnsiUnbroken coloredMatch) endWithSpace + in Line.Completion s (P.toANSI 0 coloredMatch) endWithSpace -- discards formatting in favor of better alignment --- prettyCompletion (s, p) = Line.Completion s (P.toPlainUnbroken p) True +-- prettyCompletion (s, p) = Line.Completion s (P.toPlain 0 p) True -- preserves formatting, but Haskeline doesn't know how to align prettyCompletion :: Bool -> (String, P.Pretty P.ColorText) -> Line.Completion -prettyCompletion endWithSpace (s, p) = Line.Completion s (P.toAnsiUnbroken p) endWithSpace +prettyCompletion endWithSpace (s, p) = Line.Completion s (P.toANSI 0 p) endWithSpace -- | Constructs a list of 'Completion's from a query and completion options by -- filtering them for prefix matches. A completion will be selected if it's an exact match for diff --git a/unison-cli/src/Unison/CommandLine/InputPatterns.hs b/unison-cli/src/Unison/CommandLine/InputPatterns.hs index 4f26899eaa..196e7072fb 100644 --- a/unison-cli/src/Unison/CommandLine/InputPatterns.hs +++ b/unison-cli/src/Unison/CommandLine/InputPatterns.hs @@ -4172,7 +4172,7 @@ projectToCompletion :: Sqlite.Project -> Completion projectToCompletion project = Completion { replacement = stringProjectName, - display = P.toAnsiUnbroken (prettyProjectNameSlash (project ^. #name)), + display = P.toANSI 0 (prettyProjectNameSlash (project ^. #name)), isFinished = False } where @@ -4182,7 +4182,7 @@ projectBranchToCompletion :: ProjectName -> (ProjectBranchId, ProjectBranchName) projectBranchToCompletion projectName (_, branchName) = Completion { replacement = Text.unpack (into @Text (ProjectAndBranch projectName branchName)), - display = P.toAnsiUnbroken (prettySlashProjectBranchName branchName), + display = P.toANSI 0 (prettySlashProjectBranchName branchName), isFinished = False } @@ -4212,7 +4212,7 @@ currentProjectBranchToCompletion :: (ProjectBranchId, ProjectBranchName) -> Comp currentProjectBranchToCompletion (_, branchName) = Completion { replacement = '/' : Text.unpack (into @Text branchName), - display = P.toAnsiUnbroken (prettySlashProjectBranchName branchName), + display = P.toANSI 0 (prettySlashProjectBranchName branchName), isFinished = False } @@ -4258,7 +4258,7 @@ branchRelativePathSuggestions config inputStr codebase _httpClient pp = do projectBranchToCompletionWithSep projectName (_, branchName) = Completion { replacement = Text.unpack (into @Text (ProjectAndBranch projectName branchName) <> branchPathSep), - display = P.toAnsiUnbroken (prettySlashProjectBranchName branchName <> branchPathSepPretty), + display = P.toANSI 0 (prettySlashProjectBranchName branchName <> branchPathSepPretty), isFinished = False } @@ -4266,14 +4266,14 @@ branchRelativePathSuggestions config inputStr codebase _httpClient pp = do prefixPathSep c = c { Line.replacement = branchPathSep <> Line.replacement c, - Line.display = P.toAnsiUnbroken branchPathSepPretty <> Line.display c + Line.display = P.toANSI 0 branchPathSepPretty <> Line.display c } suffixPathSep :: Completion -> Completion suffixPathSep c = c { Line.replacement = Line.replacement c <> branchPathSep, - Line.display = Line.display c <> P.toAnsiUnbroken branchPathSepPretty + Line.display = Line.display c <> P.toANSI 0 branchPathSepPretty } addBranchPrefix :: @@ -4293,7 +4293,7 @@ branchRelativePathSuggestions config inputStr codebase _httpClient pp = do in \c -> c { Line.replacement = Text.unpack prefixText <> branchPathSep <> Line.replacement c, - Line.display = P.toAnsiUnbroken (prefixPretty <> branchPathSepPretty) <> Line.display c + Line.display = P.toANSI 0 (prefixPretty <> branchPathSepPretty) <> Line.display c } branchPathSepPretty = P.hiBlack branchPathSep @@ -4374,7 +4374,7 @@ projectNameSuggestions slash (Text.strip . Text.pack -> input) codebase = do in \project -> Completion { replacement = Text.unpack (toText project), - display = P.toAnsiUnbroken (toPretty (project ^. #name)), + display = P.toANSI 0 (toPretty (project ^. #name)), isFinished = False } diff --git a/unison-cli/src/Unison/LSP/Completion.hs b/unison-cli/src/Unison/LSP/Completion.hs index 3684336bd2..fca77786c4 100644 --- a/unison-cli/src/Unison/LSP/Completion.hs +++ b/unison-cli/src/Unison/LSP/Completion.hs @@ -317,7 +317,7 @@ completionItemResolveHandler message respond = do case dep of LD.TermReferent ref -> do typ <- LSPQ.getTypeOfReferent fileUri ref - let renderedType = ": " <> (Text.pack $ TypePrinter.prettyStr (Just typeWidth) (PPED.suffixifiedPPE pped) typ) + let renderedType = ": " <> (Text.pack $ TypePrinter.prettyStr typeWidth (PPED.suffixifiedPPE pped) typ) let doc = toMarkup (Text.unlines $ ["``` unison", Name.toText fullyQualifiedName, "```"] ++ renderedDocs) pure $ (completion {_detail = Just renderedType, _documentation = Just doc} :: CompletionItem) LD.TypeReference ref -> diff --git a/unison-cli/src/Unison/LSP/FileAnalysis.hs b/unison-cli/src/Unison/LSP/FileAnalysis.hs index 2587ad5ccf..8775ffd00c 100644 --- a/unison-cli/src/Unison/LSP/FileAnalysis.hs +++ b/unison-cli/src/Unison/LSP/FileAnalysis.hs @@ -401,7 +401,7 @@ analyseNotes fileUri ppe src notes = do nameResolutionCodeActions :: [Diagnostic] -> [Context.Suggestion Symbol Ann] -> [RangedCodeAction] nameResolutionCodeActions diags suggestions = do Context.Suggestion {suggestionName, suggestionType, suggestionMatch} <- sortOn nameResolutionSuggestionPriority suggestions - let prettyType = TypePrinter.prettyStr Nothing ppe suggestionType + let prettyType = TypePrinter.prettyStr 0 ppe suggestionType let ranges = (diags ^.. folded . range) let rca = rangedCodeAction ("Use " <> Name.toText suggestionName <> " : " <> Text.pack prettyType) diags ranges pure $ @@ -424,7 +424,7 @@ analyseNotes fileUri ppe src notes = do forMaybe (toList refs) $ \ref -> runMaybeT $ do hqNameSuggestion <- MaybeT . pure $ PPE.terms ppe ref typ <- MaybeT . liftIO . Codebase.runTransaction codebase $ Codebase.getTypeOfReferent codebase ref - let prettyType = TypePrinter.prettyStr Nothing ppe typ + let prettyType = TypePrinter.prettyStr 0 ppe typ let txtName = HQ'.toText hqNameSuggestion let ranges = (diags ^.. folded . range) let rca = rangedCodeAction ("Use " <> txtName <> " : " <> Text.pack prettyType) diags ranges diff --git a/unison-cli/src/Unison/LSP/Hover.hs b/unison-cli/src/Unison/LSP/Hover.hs index 5adc770bf0..ad61980601 100644 --- a/unison-cli/src/Unison/LSP/Hover.hs +++ b/unison-cli/src/Unison/LSP/Hover.hs @@ -106,7 +106,7 @@ hoverInfo uri pos = renderTypeSigForHover :: (Var v) => PPED.PrettyPrintEnvDecl -> Text -> Type.Type v a -> Text renderTypeSigForHover pped name typ = - let renderedType = Text.pack $ TypePrinter.prettyStr (Just prettyWidth) (PPED.suffixifiedPPE pped) typ + let renderedType = Text.pack $ TypePrinter.prettyStr prettyWidth (PPED.suffixifiedPPE pped) typ in markdownify (name <> " : " <> renderedType) hoverInfoForLiteral :: MaybeT m Text diff --git a/unison-cli/src/Unison/Main.hs b/unison-cli/src/Unison/Main.hs index 1267302b85..a92e3511f2 100644 --- a/unison-cli/src/Unison/Main.hs +++ b/unison-cli/src/Unison/Main.hs @@ -87,7 +87,7 @@ import Unison.LSP.Util.Signal qualified as Signal import Unison.Parser.Ann (Ann) import Unison.Prelude import Unison.PrettyTerminal qualified as PT -import Unison.Runtime.Exception (RuntimeExn (..)) +import Unison.Runtime.Exception (prettyRuntimeExnSansCtx) import Unison.Runtime.Interface qualified as RTI import Unison.Server.Backend qualified as Backend import Unison.Server.CodebaseServer qualified as Server @@ -226,13 +226,14 @@ main version = do Run (RunCompiled file) args -> BL.readFile file >>= \bs -> try (evaluate $ RTI.decodeStandalone bs) >>= \case - Left (PE _cs err) -> do + Left re -> do + exnMessage <- prettyRuntimeExnSansCtx re exitError . P.lines $ [ P.wrap . P.text $ "I was unable to parse this file as a compiled\ \ program. The parser generated the following error:", "", - P.indentN 2 $ err + P.indentN 2 exnMessage ] Right (Left err) -> exitError . P.lines $ @@ -242,10 +243,6 @@ main version = do "", P.indentN 2 . P.wrap $ P.string err ] - Left _ -> do - exitError . P.wrap . P.text $ - "I was unable to parse this file as a compiled\ - \ program. The parser generated an unrecognized error." Right (Right (v, rf, combIx, sto)) | not vmatch -> mismatchMsg | otherwise -> diff --git a/unison-runtime/package.yaml b/unison-runtime/package.yaml index 4cf83f10c9..238ffd57a9 100644 --- a/unison-runtime/package.yaml +++ b/unison-runtime/package.yaml @@ -59,6 +59,7 @@ library: - directory - exceptions - filepath + - github - iproute - lens - memory diff --git a/unison-runtime/src/Unison/Runtime/ANF.hs b/unison-runtime/src/Unison/Runtime/ANF.hs index 4507858055..e51ab5d31e 100644 --- a/unison-runtime/src/Unison/Runtime/ANF.hs +++ b/unison-runtime/src/Unison/Runtime/ANF.hs @@ -31,8 +31,6 @@ module Unison.Runtime.ANF pattern TBinds, pattern TShift, pattern TMatch, - CompileExn (..), - internalBug, Mem (..), Lit (..), Cacheability (..), @@ -94,7 +92,6 @@ module Unison.Runtime.ANF ) where -import Control.Exception (throw) import Control.Lens (snoc, unsnoc) import Control.Monad.Reader (ReaderT (..), ask, local) import Control.Monad.State (MonadState (..), State, gets, modify, runState) @@ -105,7 +102,6 @@ import Data.List hiding (and, or) import Data.Map qualified as Map import Data.Set qualified as Set import Data.Text qualified as Data.Text -import GHC.Stack (CallStack, callStack) import Unison.ABT qualified as ABT import Unison.ABT.Normalized qualified as ABTN import Unison.Blank (nameb) @@ -119,6 +115,7 @@ import Unison.Reference (Id, Reference, Reference' (Builtin, DerivedId)) import Unison.Referent (Referent, pattern Con, pattern Ref) import Unison.Runtime.Array qualified as PA import Unison.Runtime.Foreign.Function.Type (ForeignFunc (..)) +import Unison.Runtime.InternalError (internalBug) import Unison.Runtime.TypeTags (CTag (..), PackedTag (..), RTag (..), Tag (..), maskTags, packTags, unpackTags) import Unison.Symbol (Symbol) import Unison.Term hiding (List, Ref, Text, arity, float, fresh, resolve) @@ -126,21 +123,11 @@ import Unison.Type qualified as Ty import Unison.Typechecker.Components (minimize') import Unison.Util.Bytes (Bytes) import Unison.Util.EnumContainers as EC -import Unison.Util.Pretty qualified as Pretty import Unison.Util.Text qualified as Util.Text import Unison.Var (Var, typed) import Unison.Var qualified as Var import Prelude hiding (abs, and, or, seq) --- For internal errors -data CompileExn = CE CallStack (Pretty.Pretty Pretty.ColorText) - deriving (Show) - -instance Exception CompileExn - -internalBug :: (HasCallStack) => String -> a -internalBug = throw . CE callStack . Pretty.lit . fromString - closure :: (Var v) => Map v (Set v, Set v) -> Map v (Set v) closure m0 = trace (snd <$> m0) where @@ -747,7 +734,7 @@ minimizeCyclesOrCrash :: (Var v, Ord a) => Term v a -> Term v a minimizeCyclesOrCrash t = case minimize' t of Right t -> t Left e -> - internalBug $ + internalBug [] $ "tried to minimize let rec with duplicate definitions: " ++ show (fst <$> toList e) @@ -1240,26 +1227,24 @@ instance Semigroup (BranchAccum v) where AccumSeqView el (eml <|> Just emr) cnl AccumSeqView el eml cnl <> AccumSeqView er emr _ | el /= er = - internalBug "AccumSeqView: trying to merge views of opposite ends" + internalBug [] "AccumSeqView: trying to merge views of opposite ends" | otherwise = AccumSeqView el (eml <|> emr) cnl AccumSeqView _ _ _ <> AccumDefault _ = - internalBug "seq views may not have defaults" + internalBug [] "seq views may not have defaults" AccumDefault _ <> AccumSeqView _ _ _ = - internalBug "seq views may not have defaults" + internalBug [] "seq views may not have defaults" AccumSeqSplit el nl dl bl <> AccumSeqSplit er nr dr _ | el /= er = - internalBug - "AccumSeqSplit: trying to merge splits at opposite ends" + internalBug [] "AccumSeqSplit: trying to merge splits at opposite ends" | nl /= nr = - internalBug - "AccumSeqSplit: trying to merge splits at different positions" + internalBug [] "AccumSeqSplit: trying to merge splits at different positions" | otherwise = AccumSeqSplit el nl (dl <|> dr) bl AccumDefault dl <> AccumSeqSplit er nr _ br = AccumSeqSplit er nr (Just dl) br AccumSeqSplit el nl dl bl <> AccumDefault dr = AccumSeqSplit el nl (dl <|> Just dr) bl - _ <> _ = internalBug $ "cannot merge data cases for different types" + _ <> _ = internalBug [] "cannot merge data cases for different types" instance Monoid (BranchAccum e) where mempty = AccumEmpty @@ -1748,7 +1733,7 @@ toSuperNormal :: (Var v) => Term v a -> ANFM v (SuperNormal v) toSuperNormal tm = do grp <- groupVars if not . Set.null . (Set.\\ grp) $ freeVars tm - then internalBug $ "free variables in supercombinator: " ++ show tm + then internalBug [] $ "free variables in supercombinator: " ++ show tm else Lambda (BX <$ vs) . ABTN.TAbss vs . snd <$> bindLocal vs (anfTerm body) @@ -1961,7 +1946,7 @@ anfBlock (Handle' h body) = (ctx, (_, TVar v)) | floatableCtx ctx -> do pure (hctx <> ctx, (Indirect (), TApp (FVar vh) [v])) p@(_, _) -> - internalBug $ "handle body should be a simple call: " ++ show p + internalBug [] $ "handle body should be a simple call: " ++ show p anfBlock (Match' scrut cas) = do (sctx, sc) <- anfBlock scrut (cx, v) <- contextualize sc @@ -1970,7 +1955,7 @@ anfBlock (Match' scrut cas) = do AccumDefault (TBinds (directed -> dctx) df) -> do pure (sctx <> cx <> dctx, pure df) AccumRequest _ Nothing -> - internalBug "anfBlock: AccumRequest without default" + internalBug [] "anfBlock: AccumRequest without default" AccumPure (ABTN.TAbss us bd) | [u] <- us, TBinds (directed -> bx) bd <- bd -> @@ -1980,8 +1965,8 @@ anfBlock (Match' scrut cas) = do pure (sctx <> pure [ST1 d0 u BX (TFrc v)] <> bx, pure bd) (d0, [ST1 d1 _ BX tm]) -> pure (sctx <> (d0, [ST1 d1 u BX tm]) <> bx, pure bd) - _ -> internalBug "anfBlock|AccumPure: impossible" - | otherwise -> internalBug "pure handler with too many variables" + _ -> internalBug [] "anfBlock|AccumPure: impossible" + | otherwise -> internalBug [] "pure handler with too many variables" AccumRequest abr (Just df) -> do (r, vs) <- do r <- fresh @@ -1995,7 +1980,7 @@ anfBlock (Match' scrut cas) = do let (d, msc) | (d, [ST1 _ _ BX tm]) <- cx = (d, tm) | (_, [ST _ _ _ _]) <- cx = - internalBug "anfBlock: impossible" + internalBug [] "anfBlock: impossible" | otherwise = (Indirect (), TFrc v) pure ( sctx <> pure [LZ hv (Right r) vs], @@ -2008,7 +1993,7 @@ anfBlock (Match' scrut cas) = do AccumData r df cs -> pure (sctx <> cx, pure . TMatch v $ MatchData r cs df) AccumSeqEmpty _ -> - internalBug "anfBlock: non-exhaustive AccumSeqEmpty" + internalBug [] "anfBlock: non-exhaustive AccumSeqEmpty" AccumSeqView en (Just em) bd -> do r <- fresh let op @@ -2029,7 +2014,7 @@ anfBlock (Match' scrut cas) = do ) ) AccumSeqView {} -> - internalBug "anfBlock: non-exhaustive AccumSeqView" + internalBug [] "anfBlock: non-exhaustive AccumSeqView" AccumSeqSplit en n mdf bd -> do i <- fresh r <- fresh @@ -2120,7 +2105,7 @@ anfBlock (TypeLink' r) = pure (mempty, pure . TLit $ LY r) anfBlock (List' as) = fmap (pure . TPrm BLDS) <$> anfArgs tms where tms = toList as -anfBlock t = internalBug $ "anf: unhandled term: " ++ show t +anfBlock t = internalBug [] $ "anf: unhandled term: " ++ show t -- Note: this assumes that patterns have already been translated -- to a state in which every case matches a single layer of data, @@ -2132,7 +2117,7 @@ anfInitCase :: MatchCase p (Term v a) -> ANFD v (BranchAccum v) anfInitCase u (MatchCase p guard (ABT.AbsN' vs bd)) - | Just _ <- guard = internalBug "anfInitCase: unexpected guard" + | Just _ <- guard = internalBug [] "anfInitCase: unexpected guard" | P.Unbound _ <- p, [] <- vs = AccumDefault <$> anfBody bd @@ -2140,7 +2125,7 @@ anfInitCase u (MatchCase p guard (ABT.AbsN' vs bd)) [v] <- vs = AccumDefault . ABTN.rename v u <$> anfBody bd | P.Var _ <- p = - internalBug $ "vars: " ++ show (length vs) + internalBug [] $ "vars: " ++ show (length vs) | P.Int _ (fromIntegral -> i) <- p = AccumIntegral Ty.intRef Nothing . EC.mapSingleton i <$> anfBody bd | P.Nat _ i <- p = @@ -2175,7 +2160,7 @@ anfInitCase u (MatchCase p guard (ABT.AbsN' vs bd)) <*> anfBody bd <&> \(exp, kf, bd) -> let (us, uk) = - maybe (internalBug "anfInitCase: unsnoc impossible") id $ + maybe (internalBug [] "anfInitCase: unsnoc impossible") id $ unsnoc exp jn = Builtin "jumpCont" in flip AccumRequest Nothing @@ -2204,7 +2189,7 @@ anfInitCase u (MatchCase p guard (ABT.AbsN' vs bd)) where anfBody tm = Compose . bindLocal vs $ anfTerm tm anfInitCase _ (MatchCase p _ _) = - internalBug $ "anfInitCase: unexpected pattern: " ++ show p + internalBug [] $ "anfInitCase: unexpected pattern: " ++ show p valueTermLinks :: Value -> [Reference] valueTermLinks = Set.toList . valueLinks f @@ -2365,7 +2350,7 @@ expandBindings' _ _ _ = expandBindings :: (Var v) => [P.Pattern p] -> [v] -> ANFD v [v] expandBindings ps vs = Compose . state $ \(fr, bnd, co) -> case expandBindings' fr ps vs of - Left err -> internalBug $ err ++ " " ++ show (ps, vs) + Left err -> internalBug [] $ err ++ " " ++ show (ps, vs) Right (fr, l) -> (pure l, (fr, bnd, co)) anfCases :: @@ -2424,8 +2409,8 @@ prettyLVars (c : cs) (v : vs) = showString " " . showParen True (pvar v . showString ":" . shows c) . prettyLVars cs vs -prettyLVars [] (_ : _) = internalBug "more variables than conventions" -prettyLVars (_ : _) [] = internalBug "more conventions than variables" +prettyLVars [] (_ : _) = internalBug [] "more variables than conventions" +prettyLVars (_ : _) [] = internalBug [] "more conventions than variables" prettyRBind :: (Var v) => [v] -> ShowS prettyRBind [] = showString "()" diff --git a/unison-runtime/src/Unison/Runtime/ANF/Serialize.hs b/unison-runtime/src/Unison/Runtime/ANF/Serialize.hs index 4b0759ad0f..4fd3118a52 100644 --- a/unison-runtime/src/Unison/Runtime/ANF/Serialize.hs +++ b/unison-runtime/src/Unison/Runtime/ANF/Serialize.hs @@ -24,7 +24,7 @@ import GHC.Stack import Unison.ABT.Normalized (Term (..)) import Unison.Reference (Reference, Reference' (Builtin), pattern Derived) import Unison.Runtime.ANF as ANF hiding (Tag) -import Unison.Runtime.Exception +import Unison.Runtime.Exception (exn) import Unison.Runtime.Foreign.Function.Type (ForeignFunc) import Unison.Runtime.Serialize import Unison.Util.Text qualified as Util.Text @@ -256,7 +256,7 @@ index ctx u = go 0 ctx | otherwise = go (n + 1) vs deindex :: (HasCallStack) => [v] -> Word64 -> v -deindex [] _ = exn "deindex: bad index" +deindex [] _ = exn [] "deindex: bad index" deindex (v : vs) n | n == 0 = v | otherwise = deindex vs (n - 1) @@ -273,7 +273,7 @@ getIndex = unVarInt <$> deserialize putVar :: (MonadPut m) => (Eq v) => [v] -> v -> m () putVar ctx v | Just i <- index ctx v = putIndex i - | otherwise = exn "putVar: variable not in context" + | otherwise = exn [] "putVar: variable not in context" getVar :: (MonadGet m) => [v] -> m v getVar ctx = deindex ctx <$> getIndex @@ -297,7 +297,7 @@ getCCs = getWord8 <&> \case 0 -> UN 1 -> BX - _ -> exn "getCCs: bad calling convention" + _ -> exn [] "getCCs: bad calling convention" -- Serializes a `SuperGroup`. -- @@ -357,7 +357,7 @@ getCacheability = getWord8 >>= \case 0 -> pure Uncacheable 1 -> pure Cacheable - n -> exn $ "getBLit: unrecognized cacheability byte: " ++ show n + n -> exn [] $ "getBLit: unrecognized cacheability byte: " ++ show n putComb :: (MonadPut m) => @@ -430,7 +430,7 @@ putNormal refrep fops ctx tm = case tm of *> putCCs ccs *> putNormal refrep fops ctx l *> putNormal refrep fops (pushCtx us ctx) e - _ -> exn "putNormal: malformed term" + _ -> exn [] "putNormal: malformed term" getNormal :: (MonadGet m) => (Var v) => [v] -> Word64 -> m (ANormal v) getNormal ctx frsh0 = @@ -499,7 +499,7 @@ putFunc refrep fops ctx f = case f of | Just nm <- Map.lookup f fops -> putTag FForeignT *> putText nm | otherwise -> - exn $ "putFunc: could not serialize foreign operation: " ++ show f + exn [] $ "putFunc: could not serialize foreign operation: " ++ show f getFunc :: (MonadGet m) => (Var v) => [v] -> m (Func v) getFunc ctx = @@ -510,18 +510,18 @@ getFunc ctx = FConT -> FCon <$> getReference <*> getCTag FReqT -> FReq <$> getReference <*> getCTag FPrimT -> FPrim . Left <$> getPOp - FForeignT -> exn "getFunc: can't deserialize a foreign func" + FForeignT -> exn [] "getFunc: can't deserialize a foreign func" putPOp :: (MonadPut m) => POp -> m () putPOp op | Just w <- Map.lookup op pop2word = putWord16be w - | otherwise = exn $ "putPOp: unknown POp: " ++ show op + | otherwise = exn [] $ "putPOp: unknown POp: " ++ show op getPOp :: (MonadGet m) => m POp getPOp = getWord16be >>= \w -> case Map.lookup w word2pop of Just op -> pure op - Nothing -> exn "getPOp: unknown enum code" + Nothing -> exn [] "getPOp: unknown enum code" pOpCode :: POp -> Word16 pOpCode op = case op of @@ -788,7 +788,7 @@ putBranches refrep fops ctx bs = case bs of putReference r putEnumMap putWord64be (putNormal refrep fops ctx) m putMaybe df $ putNormal refrep fops ctx - _ -> exn "putBranches: malformed intermediate term" + _ -> exn [] "putBranches: malformed intermediate term" getBranches :: (MonadGet m) => (Var v) => [v] -> Word64 -> m (Branched (ANormal v)) @@ -933,7 +933,7 @@ getValue v = where assertEmptyUnboxed :: (MonadGet m) => [a] -> m () assertEmptyUnboxed [] = pure () - assertEmptyUnboxed _ = exn "getValue: unboxed values no longer supported" + assertEmptyUnboxed _ = exn [] "getValue: unboxed values no longer supported" putCont :: (MonadPut m) => Version -> Cont -> m () putCont _ KE = putTag KET @@ -987,7 +987,7 @@ getCont v = <*> getCont v where assert0 _name 0 = pure () - assert0 name n = exn $ "getCont: malformed intermediate term. Expected " <> name <> " to be 0, but got " <> show n + assert0 name n = exn [] $ "getCont: malformed intermediate term. Expected " <> name <> " to be 0, but got " <> show n deserializeCode :: ByteString -> Either String Code deserializeCode bs = runGetS (getVersion >>= getCode) bs diff --git a/unison-runtime/src/Unison/Runtime/Exception.hs b/unison-runtime/src/Unison/Runtime/Exception.hs index 2e79c163bd..19eea19053 100644 --- a/unison-runtime/src/Unison/Runtime/Exception.hs +++ b/unison-runtime/src/Unison/Runtime/Exception.hs @@ -1,34 +1,197 @@ module Unison.Runtime.Exception - ( RuntimeExn (..), + ( module InternalError, + RuntimeExn (BU, PE), + bugMsg, die, dieP, exn, + listErrors, + tabulateErrors, + peStr, + prettyRuntimeExn, + prettyRuntimeExnSansCtx, ) where -import Control.Exception -import Data.String (fromString) -import Data.Text -import GHC.Stack +import Control.Exception (throw, throwIO) +import Data.Text (isPrefixOf) +import GHC.Stack (CallStack, callStack) +import Unison.Builtin.Decls qualified as RF +import Unison.Codebase.Runtime (Error) +import Unison.Prelude +import Unison.PrettyPrintEnv (PrettyPrintEnv) +import Unison.PrettyPrintEnv qualified as PPE import Unison.Reference (Reference) -import Unison.Runtime.Stack +import Unison.Referent qualified as RF (pattern Ref) +import Unison.Runtime.Decompile (DecompError, DecompResult, decompile, renderDecompError) +import Unison.Runtime.InternalError as InternalError +import Unison.Runtime.Stack (Val) +import Unison.Symbol (Symbol) +import Unison.Syntax.NamePrinter (prettyHashQualified) +import Unison.Syntax.TermPrinter (pretty) +import Unison.Term qualified as Tm import Unison.Util.Pretty as P data RuntimeExn - = PE CallStack (P.Pretty P.ColorText) - | BU [(Reference, Int)] Text Val - deriving (Show) + = -- | pretty exception + PE CallStack [Word] (P.Pretty P.ColorText) + | -- | __TODO__: What is `BU`? Boxed/Unboxed? + BU [(Reference, Int)] Text Val + +prettyRuntimeExn' :: + (Applicative f) => + (Word -> f (Pretty P.ColorText)) -> + PrettyPrintEnv -> + (Reference -> Reference) -> + (Val -> DecompResult Symbol) -> + RuntimeExn -> + f (Pretty P.ColorText) +prettyRuntimeExn' issueFn ppe backmap decom = \case + PE _ issues err -> do + issueMessages <- traverse issueFn issues + pure $ + P.fatalCallout . P.lines $ + [ P.wrap "Sorry – I’ve encountered a Unison runtime error.", + "", + P.indentN 2 err, + "" + ] + <> if null issues + then [P.wrap "Please report it at https://github.com/unisonweb/unison/issues/new/choose."] + else + [ P.wrap "Please check if one of these known issues matches your situation:", + "", + P.bulleted issueMessages, + "", + P.wrap "If not, please open a new one: https://github.com/unisonweb/unison/issues/new/choose" + ] + BU tr0 nm c -> pure . bugMsg ppe tr nm $ decom c + where + tr = first backmap <$> tr0 + +prettyRuntimeExn :: + PrettyPrintEnv -> (Reference -> Reference) -> (Val -> DecompResult Symbol) -> RuntimeExn -> IO (Pretty P.ColorText) +prettyRuntimeExn = + prettyRuntimeExn' + ( \i -> do + mtitle <- githubTitleForIssue i + pure $ either (const $ issueUrl i) (\title -> P.wrap $ P.text title <> " " <> issueUrl i) mtitle + ) + +bugMsg :: + PrettyPrintEnv -> + [(Reference, Int)] -> + Text -> + (Set DecompError, Tm.Term Symbol ()) -> + Pretty ColorText +bugMsg ppe tr name (errs, tm) + | name == "blank expression" = + P.callout icon . P.linesNonEmpty $ + [ P.wrap $ "I encountered a" <> P.red (P.text name) <> "with the following name/message:", + "", + P.indentN 2 $ pretty ppe tm, + tabulateErrors errs, + stackTrace ppe tr + ] + | "pattern match failure" `isPrefixOf` name = + P.callout icon . P.linesNonEmpty $ + [ P.wrap $ "I've encountered a" <> P.red (P.text name) <> "while scrutinizing:", + "", + P.indentN 2 $ pretty ppe tm, + "", + P.wrap "This happens when calling a function that doesn't handle all possible inputs", + tabulateErrors errs, + stackTrace ppe tr + ] + | name == "builtin.raise" = + P.callout icon . P.linesNonEmpty $ + [ P.wrap ("The program halted with an unhandled exception:"), + "", + P.indentN 2 $ pretty ppe tm, + tabulateErrors errs, + stackTrace ppe tr + ] + | name == "builtin.bug", + RF.TupleTerm' [Tm.Text' msg, x] <- tm, + "pattern match failure" `isPrefixOf` msg = + P.callout icon . P.linesNonEmpty $ + [ P.wrap $ "I've encountered a" <> P.red (P.text msg) <> "while scrutinizing:", + "", + P.indentN 2 $ pretty ppe x, + "", + P.wrap "This happens when calling a function that doesn't handle all possible inputs", + tabulateErrors errs, + stackTrace ppe tr + ] + | otherwise = + P.callout icon . P.linesNonEmpty $ + [ P.wrap $ "I've encountered a call to" <> P.red (P.text name) <> "with the following value:", + "", + P.indentN 2 $ pretty ppe tm, + tabulateErrors errs, + stackTrace ppe tr + ] + +stackTrace :: PrettyPrintEnv -> [(Reference, Int)] -> Pretty ColorText +stackTrace _ [] = mempty +stackTrace ppe tr = "\nStack trace:\n" <> P.indentN 2 (P.lines $ f <$> tr) + where + f (rf, n) = name <> count + where + count + | n > 1 = " (" <> fromString (show n) <> " copies)" + | otherwise = "" + name = + syntaxToColor + . prettyHashQualified + . PPE.termName ppe + . RF.Ref + $ rf + +icon :: Pretty ColorText +icon = "💔💥" + +listErrors :: Set DecompError -> [Error] +listErrors = fmap (P.indentN 2 . renderDecompError) . toList + +tabulateErrors :: Set DecompError -> Error +tabulateErrors errs | null errs = mempty +tabulateErrors errs = + P.indentN 2 . P.lines $ + "" + : P.wrap "The following errors occured while decompiling:" + : (listErrors errs) + +prettyRuntimeExnSansCtx :: RuntimeExn -> IO (Pretty P.ColorText) +prettyRuntimeExnSansCtx = prettyRuntimeExn mempty id (decompile pure \_ _ -> Nothing) + +-- | __TODO__: With GHC 9.10, this implementation can be moved to `displayException` on the `Exception` instance, and +-- this instance can be derived again (see haskell/core-libraries-committee#198). +instance Show RuntimeExn where + show = P.toPlain 0 . runIdentity . prettyRuntimeExn' (pure . issueUrl) mempty id (decompile pure \_ _ -> Nothing) instance Exception RuntimeExn -die :: (HasCallStack) => String -> IO a -die = throwIO . PE callStack . P.lit . fromString +peStr :: (HasCallStack) => [Word] -> String -> RuntimeExn +peStr issues = PE callStack issues . P.lit . fromString +{-# INLINE peStr #-} + +die :: (HasCallStack) => [Word] -> String -> IO a +die issues s = do + void . throwIO $ peStr issues s + -- This is unreachable, but we need it to fix some quirks in GHC's + -- worker/wrapper optimization, specifically, it seems that when throwIO's polymorphic return + -- value is specialized to a type like 'Stack' which we want GHC to unbox, it will sometimes + -- fail to unbox it, possibly because it can't unbox it when it's strictly a type application. + -- For whatever reason, this seems to fix it while still allowing us to throw exceptions in IO + -- like we prefer. + error "unreachable" {-# INLINE die #-} -dieP :: (HasCallStack) => P.Pretty P.ColorText -> IO a -dieP = throwIO . PE callStack +dieP :: (HasCallStack) => [Word] -> P.Pretty P.ColorText -> IO a +dieP issues = throwIO . PE callStack issues {-# INLINE dieP #-} -exn :: (HasCallStack) => String -> a -exn = throw . PE callStack . P.lit . fromString +exn :: (HasCallStack) => [Word] -> String -> a +exn issues = throw . peStr issues {-# INLINE exn #-} diff --git a/unison-runtime/src/Unison/Runtime/Foreign/Function.hs b/unison-runtime/src/Unison/Runtime/Foreign/Function.hs index 3e94e928a6..6131143ecb 100644 --- a/unison-runtime/src/Unison/Runtime/Foreign/Function.hs +++ b/unison-runtime/src/Unison/Runtime/Foreign/Function.hs @@ -151,7 +151,7 @@ import Unison.Runtime.ANF.Serialize qualified as ANF import Unison.Runtime.Array qualified as PA import Unison.Runtime.Builtin import Unison.Runtime.Crypto.Rsa qualified as Rsa -import Unison.Runtime.Exception +import Unison.Runtime.Exception (die) import Unison.Runtime.Foreign hiding (Failure) import Unison.Runtime.Foreign qualified as F import Unison.Runtime.Foreign.Function.Type @@ -815,12 +815,12 @@ foreignCallHelper = \case Text_patterns_charIn -> mkForeign $ \ccs -> do cs <- for ccs $ \case CharVal c -> pure c - _ -> die "Text.patterns.charIn: non-character closure" + _ -> die [] "Text.patterns.charIn: non-character closure" evaluate . TPat.cpattern . TPat.Char $ TPat.CharSet cs Text_patterns_notCharIn -> mkForeign $ \ccs -> do cs <- for ccs $ \case CharVal c -> pure c - _ -> die "Text.patterns.notCharIn: non-character closure" + _ -> die [] "Text.patterns.notCharIn: non-character closure" evaluate . TPat.cpattern . TPat.Char . TPat.Not $ TPat.CharSet cs Pattern_many -> mkForeign $ \(TPat.CP p _) -> evaluate . TPat.cpattern $ TPat.Many False p @@ -854,7 +854,7 @@ foreignCallHelper = \case Char_Class_anyOf -> mkForeign $ \ccs -> do cs <- for ccs $ \case CharVal c -> pure c - _ -> die "Text.patterns.charIn: non-character closure" + _ -> die [] "Text.patterns.charIn: non-character closure" evaluate $ TPat.CharSet cs Char_Class_alphanumeric -> mkForeign $ \() -> pure (TPat.CharClass TPat.AlphaNum) Char_Class_upper -> mkForeign $ \() -> pure (TPat.CharClass TPat.Upper) @@ -911,19 +911,19 @@ foreignCallHelper = \case (r :: Map Val Val) <- decodeVal vr m <- evaluate $ Map.union l r pure . Data1 Ty.setRef TT.setWrapTag $ encodeVal m - _ -> die "Set.union: bad closure" + _ -> die [] "Set.union: bad closure" Set_intersect -> mkForeign $ \case (Data1 _ _ vl, Data1 _ _ vr) -> do (l :: Map Val Val) <- decodeVal vl (r :: Map Val Val) <- decodeVal vr m <- evaluate $ Map.intersection l r pure . Data1 Ty.setRef TT.setWrapTag $ encodeVal m - _ -> die "Set.insersect: bad closure" + _ -> die [] "Set.insersect: bad closure" Set_toList -> mkForeign $ \case (Data1 _ _ vs) -> do (s :: Map Val Val) <- decodeVal vs evaluate . forceListSpine $ Map.keys s - _ -> die "Set.toList: bad closure" + _ -> die [] "Set.toList: bad closure" where forceListSpine xs = foldl (\u x -> x `seq` u) xs xs chop = reverse . dropWhile isPathSeparator . reverse diff --git a/unison-runtime/src/Unison/Runtime/IOSource.hs b/unison-runtime/src/Unison/Runtime/IOSource.hs index f690671fc5..571c1ea18a 100644 --- a/unison-runtime/src/Unison/Runtime/IOSource.hs +++ b/unison-runtime/src/Unison/Runtime/IOSource.hs @@ -58,7 +58,7 @@ typecheckingEnv = parsedFile :: UF.UnisonFile Symbol Ann parsedFile = case runIdentity (Parsers.parseFile "" sourceString parsingEnv) of - Left err -> error (Pretty.toAnsiUnbroken (PrintError.prettyParseError sourceString err)) + Left err -> error (Pretty.toANSI 0 (PrintError.prettyParseError sourceString err)) Right file -> file typecheckedFile :: UF.TypecheckedUnisonFile Symbol Ann diff --git a/unison-runtime/src/Unison/Runtime/Interface.hs b/unison-runtime/src/Unison/Runtime/Interface.hs index a2fb960131..85b22f5fb6 100644 --- a/unison-runtime/src/Unison/Runtime/Interface.hs +++ b/unison-runtime/src/Unison/Runtime/Interface.hs @@ -29,10 +29,10 @@ module Unison.Runtime.Interface where import Control.Concurrent.STM as STM -import Control.Exception (throwIO) import Control.Monad import Control.Monad.State import Data.Binary.Get (runGetOrFail) +import Data.Bitraversable (bitraverse) import Data.ByteString qualified as BS import Data.ByteString.Lazy qualified as BL import Data.Bytes.Get (MonadGet, getWord8, runGetS) @@ -53,10 +53,9 @@ import Data.Set as Set (\\), ) import Data.Set qualified as Set -import Data.Text as Text (isPrefixOf, pack, unpack) +import Data.Text as Text (pack, unpack) import Data.Void (absurd) import GHC.IO.Exception (IOErrorType (NoSuchThing, OtherError, PermissionDenied), IOException (ioe_description, ioe_type)) -import GHC.Stack (callStack) import Network.Simple.TCP (Socket, acceptFork, listen, recv, send) import Network.Socket (PortNumber, socketPort) import System.Directory @@ -106,6 +105,15 @@ import Unison.Runtime.ANF.Serialize as ANF import Unison.Runtime.Builtin import Unison.Runtime.Decompile import Unison.Runtime.Exception + ( bugMsg, + die, + dieP, + listErrors, + prettyCompileExn, + prettyRuntimeExn, + prettyRuntimeExnSansCtx, + tabulateErrors, + ) import Unison.Runtime.Foreign.Function (functionUnreplacements) import Unison.Runtime.MCode ( Args (..), @@ -147,7 +155,6 @@ import Unison.Runtime.Stack import Unison.Runtime.TypeTags qualified as TT import Unison.Symbol (Symbol) import Unison.Syntax.HashQualified qualified as HQ (toText) -import Unison.Syntax.NamePrinter (prettyHashQualified) import Unison.Syntax.TermPrinter import Unison.Term qualified as Tm import Unison.Type qualified as Type @@ -213,10 +220,10 @@ resolveTermRef :: RF.Reference -> IO (Term Symbol) resolveTermRef _ b@(RF.Builtin _) = - die $ "Unknown builtin term reference: " ++ show b + die [] $ "Unknown builtin term reference: " ++ show b resolveTermRef cl r@(RF.DerivedId i) = getTerm cl i >>= \case - Nothing -> die $ "Unknown term reference: " ++ show r + Nothing -> die [] $ "Unknown term reference: " ++ show r Just tm -> pure tm allocType :: @@ -225,7 +232,7 @@ allocType :: Either [Int] [Int] -> IO EvalCtx allocType _ b@(RF.Builtin _) _ = - die $ "Unknown builtin type reference: " ++ show b + die [] $ "Unknown builtin type reference: " ++ show b allocType ctx r cons = pure $ ctx {dspec = Map.insert r cons $ dspec ctx} @@ -571,7 +578,7 @@ ensureExists :: (HasCallStack) => CreateProcess -> (CmdSpec -> Either (Int, Stri ensureExists cmd err = ccall >>= \case Nothing -> pure () - Just failure -> dieP $ err (cmdspec cmd) failure + Just failure -> dieP [] $ err (cmdspec cmd) failure where call = readCreateProcessWithExitCode cmd "" >>= \case @@ -1011,9 +1018,9 @@ nativeCompileCodes copts executable codes base path = do pure () callout _ _ _ _ = fail "withCreateProcess didn't provide handles" ucrError (e :: IOException) = - throwIO $ PE callStack (runtimeErrMsg (cmdspec p) (Right e)) + dieP [] . runtimeErrMsg (cmdspec p) $ Right e racoError (e :: IOException) = - throwIO $ PE callStack (racoErrMsg (makeRacoCmd RawCommand) (Right e)) + dieP [] . racoErrMsg (makeRacoCmd RawCommand) $ Right e dargs = ["-G", srcPath] pargs | profile copts = "--profile" : dargs @@ -1039,11 +1046,7 @@ evalInContext ppe ctx activeThreads w = do decom = decompileCtx crs ctx finish = fmap (first listErrors . decom) - prettyError (PE _ p) = p - prettyError (BU tr0 nm c) = - bugMsg ppe tr nm $ decom c - where - tr = first (backmapRef ctx) <$> tr0 + prettyError = prettyRuntimeExn ppe (backmapRef ctx) decom debugText fancy val = case decom val of (errs, dv) @@ -1056,10 +1059,8 @@ evalInContext ppe ctx activeThreads w = do (debugTextFormat fancy $ pretty ppe dv) result <- - traverse (const $ readIORef r) - . first prettyError - <=< try - $ apply0 (Just hook) ((ccache ctx) {tracer = debugText}) activeThreads w + bitraverse prettyError (const $ readIORef r) <=< try $ + apply0 (Just hook) ((ccache ctx) {tracer = debugText}) activeThreads w pure $ finish result executeMainComb :: @@ -1074,8 +1075,7 @@ executeMainComb init cc = do Left err -> Left <$> formatErr err Right () -> pure (Right ()) where - formatErr (PE _ msg) = pure msg - formatErr (BU tr nm c) = do + formatErr re = do crs <- readTVarIO (combRefs cc) let ctx = cacheContext cc decom = @@ -1087,107 +1087,15 @@ executeMainComb init cc = do (intermedRemap ctx) (decompTm ctx) ) - pure . bugMsg PPE.empty tr nm $ decom c - -bugMsg :: - PrettyPrintEnv -> - [(Reference, Int)] -> - Text -> - (Set DecompError, Term Symbol) -> - Pretty ColorText -bugMsg ppe tr name (errs, tm) - | name == "blank expression" = - P.callout icon . P.linesNonEmpty $ - [ P.wrap - ( "I encountered a" - <> P.red (P.text name) - <> "with the following name/message:" - ), - "", - P.indentN 2 $ pretty ppe tm, - tabulateErrors errs, - stackTrace ppe tr - ] - | "pattern match failure" `isPrefixOf` name = - P.callout icon . P.linesNonEmpty $ - [ P.wrap - ( "I've encountered a" - <> P.red (P.text name) - <> "while scrutinizing:" - ), - "", - P.indentN 2 $ pretty ppe tm, - "", - "This happens when calling a function that doesn't handle all \ - \possible inputs", - tabulateErrors errs, - stackTrace ppe tr - ] - | name == "builtin.raise" = - P.callout icon . P.linesNonEmpty $ - [ P.wrap ("The program halted with an unhandled exception:"), - "", - P.indentN 2 $ pretty ppe tm, - tabulateErrors errs, - stackTrace ppe tr - ] - | name == "builtin.bug", - RF.TupleTerm' [Tm.Text' msg, x] <- tm, - "pattern match failure" `isPrefixOf` msg = - P.callout icon . P.linesNonEmpty $ - [ P.wrap - ( "I've encountered a" - <> P.red (P.text msg) - <> "while scrutinizing:" - ), - "", - P.indentN 2 $ pretty ppe x, - "", - "This happens when calling a function that doesn't handle all \ - \possible inputs", - tabulateErrors errs, - stackTrace ppe tr - ] -bugMsg ppe tr name (errs, tm) = - P.callout icon . P.linesNonEmpty $ - [ P.wrap - ( "I've encountered a call to" - <> P.red (P.text name) - <> "with the following value:" - ), - "", - P.indentN 2 $ pretty ppe tm, - tabulateErrors errs, - stackTrace ppe tr - ] - -stackTrace :: PrettyPrintEnv -> [(Reference, Int)] -> Pretty ColorText -stackTrace _ [] = mempty -stackTrace ppe tr = "\nStack trace:\n" <> P.indentN 2 (P.lines $ f <$> tr) - where - f (rf, n) = name <> count - where - count - | n > 1 = " (" <> fromString (show n) <> " copies)" - | otherwise = "" - name = - syntaxToColor - . prettyHashQualified - . PPE.termName ppe - . RF.Ref - $ rf - -icon :: Pretty ColorText -icon = "💔💥" + prettyRuntimeExn mempty id decom re catchInternalErrors :: IO (Either Error a) -> IO (Either Error a) catchInternalErrors sub = sub `UnliftIO.catch` hCE `UnliftIO.catch` hRE where - hCE (CE _ e) = pure $ Left e - hRE (PE _ e) = pure $ Left e - hRE (BU _ _ _) = pure $ Left "impossible" + hCE = fmap Left . prettyCompileExn + hRE = fmap Left . prettyRuntimeExnSansCtx decodeStandalone :: BL.ByteString -> @@ -1254,9 +1162,8 @@ tryM = . flip UnliftIO.catch hCE . fmap (const Nothing) where - hCE (CE _ e) = pure $ Just e - hRE (PE _ e) = pure $ Just e - hRE (BU _ _ _) = pure $ Just "impossible" + hCE = fmap Just . prettyCompileExn + hRE = fmap Just . prettyRuntimeExnSansCtx runStandalone :: Bool -> StoredCache -> CombIx -> IO (Either (Pretty ColorText) ()) runStandalone sandboxed sc init = @@ -1311,17 +1218,6 @@ debugTextFormat fancy = where render = if fancy then toANSI else toPlain -listErrors :: Set DecompError -> [Error] -listErrors = fmap (P.indentN 2 . renderDecompError) . toList - -tabulateErrors :: Set DecompError -> Error -tabulateErrors errs | null errs = mempty -tabulateErrors errs = - P.indentN 2 . P.lines $ - "" - : P.wrap "The following errors occured while decompiling:" - : (listErrors errs) - restoreCache :: Bool -> StoredCache -> IO CCache restoreCache sandboxed (SCache cs crs cacheableCombs trs ftm fty int rtm rty sbs) = do cc <- @@ -1389,7 +1285,7 @@ traceNeeded init src = go mempty init | Just co <- Map.lookup nx src = foldlM go (Map.insert nx co acc) (groupTermLinks co) | otherwise = - die $ "traceNeeded: unknown combinator: " ++ show nx + die [] $ "traceNeeded: unknown combinator: " ++ show nx buildSCache :: EnumMap Word64 Reference -> @@ -1459,4 +1355,4 @@ standalone cc init = <*> readTVarIO (refTy cc) <*> readTVarIO (sandbox cc) Nothing -> - die $ "standalone: unknown combinator: " ++ show init + die [] $ "standalone: unknown combinator: " ++ show init diff --git a/unison-runtime/src/Unison/Runtime/InternalError.hs b/unison-runtime/src/Unison/Runtime/InternalError.hs new file mode 100644 index 0000000000..4912b26c97 --- /dev/null +++ b/unison-runtime/src/Unison/Runtime/InternalError.hs @@ -0,0 +1,66 @@ +{-# LANGUAGE OverloadedStrings #-} + +-- | This module is distinct from "Unison.Runtime.Exception" because that depends on "Unison.Runtime.Stack", which would +-- cause an import cycle. +module Unison.Runtime.InternalError + ( CompileExn (CE), + githubTitleForIssue, + internalBug, + issueUrl, + prettyCompileExn, + ) +where + +import Control.Exception (throw) +import GHC.Stack (CallStack, callStack) +import GitHub qualified as GH +import Unison.Prelude +import Unison.Util.Pretty as Pretty + +data CompileExn = CE CallStack [Word] String + +issueUrl :: Word -> Pretty Pretty.ColorText +issueUrl = Pretty.string . ("https://github.com/unisonweb/unison/issues/" <>) . show + +githubTitleForIssue :: Word -> IO (Either GH.Error Text) +githubTitleForIssue = + fmap (fmap GH.issueTitle) . GH.github' GH.issueR "unisonweb" "unison" . GH.IssueNumber . fromIntegral + +prettyCompileExn' :: + (Applicative f) => (Word -> f (Pretty Pretty.ColorText)) -> CompileExn -> f (Pretty Pretty.ColorText) +prettyCompileExn' issueFn (CE _ issues err) = do + issueMessages <- traverse issueFn issues + pure $ + Pretty.fatalCallout . Pretty.lines $ + [ Pretty.wrap "Sorry – I've encountered a bug in the Unison runtime.", + "", + Pretty.indentN 2 $ Pretty.string err, + "" + ] + <> if null issues + then [Pretty.wrap "Please report it at https://github.com/unisonweb/unison/issues/new/choose."] + else + [ Pretty.wrap "Please check if one of these known issues matches your situation:", + "", + Pretty.bulleted issueMessages, + "", + Pretty.wrap "If not, please open a new one: https://github.com/unisonweb/unison/issues/new/choose" + ] + +prettyCompileExn :: CompileExn -> IO (Pretty Pretty.ColorText) +prettyCompileExn = + prettyCompileExn' + ( \i -> do + mtitle <- githubTitleForIssue i + pure $ either (const $ issueUrl i) (\title -> Pretty.wrap $ Pretty.text title <> " " <> issueUrl i) mtitle + ) + +-- | __TODO__: With GHC 9.10, this implementation can be moved to `displayException` on the `Exception` instance, and +-- this instance can be derived again (see haskell/core-libraries-committee#198). +instance Show CompileExn where + show = Pretty.toPlain 0 . runIdentity . prettyCompileExn' (pure . issueUrl) + +instance Exception CompileExn + +internalBug :: (HasCallStack) => [Word] -> String -> a +internalBug issues = throw . CE callStack issues diff --git a/unison-runtime/src/Unison/Runtime/MCode.hs b/unison-runtime/src/Unison/Runtime/MCode.hs index f8cd2641d7..c61ad43130 100644 --- a/unison-runtime/src/Unison/Runtime/MCode.hs +++ b/unison-runtime/src/Unison/Runtime/MCode.hs @@ -77,7 +77,6 @@ import Unison.Runtime.ANF PackedTag (..), SuperGroup (..), SuperNormal (..), - internalBug, packTags, pattern TApp, pattern TBLit, @@ -94,6 +93,7 @@ import Unison.Runtime.ANF ) import Unison.Runtime.ANF qualified as ANF import Unison.Runtime.Foreign.Function.Type (ForeignFunc (..), foreignFuncBuiltinName) +import Unison.Runtime.InternalError (internalBug) import Unison.Util.EnumContainers as EC import Unison.Util.Text (Text) import Unison.Var (Var) @@ -282,7 +282,7 @@ argsToLists = \case VArg2 i j -> [i, j] VArgR i l -> take l [i ..] VArgN us -> primArrayToList us - VArgV _ -> internalBug "argsToLists: DArgV" + VArgV _ -> internalBug [] "argsToLists: DArgV" {-# INLINEABLE argsToLists #-} countArgs :: Args -> Int @@ -291,12 +291,12 @@ countArgs (VArg1 {}) = 1 countArgs (VArg2 {}) = 2 countArgs (VArgR _ l) = l countArgs (VArgN us) = sizeofPrimArray us -countArgs (VArgV {}) = internalBug "countArgs: DArgV" +countArgs (VArgV {}) = internalBug [] "countArgs: DArgV" {-# INLINEABLE countArgs #-} data Prim1 - -- integral - = DECI -- decrement + = -- integral + DECI -- decrement | DECN | INCI -- increment | INCN @@ -372,8 +372,8 @@ data Prim1 deriving (Show, Eq, Ord, Enum, Bounded) data Prim2 - -- integral - = ADDI -- + + = -- integral + ADDI -- + | ADDN | SUBI -- - | SUBN @@ -627,7 +627,7 @@ data RefNums = RN emptyRNs :: RefNums emptyRNs = RN mt mt (const Nothing) where - mt _ = internalBug "RefNums: empty" + mt _ = internalBug [] "RefNums: empty" type Comb = GComb Void CombIx @@ -1069,11 +1069,11 @@ emitSection rns grpr grpn rec ctx (TMatch v bs) MatchSum cs <- bs = emitSumMatching rns grpr grpn rec ctx v i cs | Just (_, cc) <- ctxResolve ctx v = - internalBug $ + internalBug [] $ "emitSection: mismatched calling convention for match: " ++ matchCallingError cc bs | otherwise = - internalBug $ + internalBug [] $ "emitSection: could not resolve match variable: " ++ show (ctx, v) emitSection rns grpr grpn rec ctx (THnd rs h b) | Just (i, BX) <- ctxResolve ctx h = @@ -1090,11 +1090,11 @@ emitSection _ _ _ _ ctx (TFrc v) | Just (i, BX) <- ctxResolve ctx v = countCtx ctx $ App False (Stk i) ZArgs | Just _ <- ctxResolve ctx v = - internalBug $ + internalBug [] $ "emitSection: values to be forced must be boxed: " ++ show v | otherwise = emitSectionVErr v emitSection _ _ _ _ _ tm = - internalBug $ "emitSection: unhandled code: " ++ show tm + internalBug [] $ "emitSection: unhandled code: " ++ show tm -- Emit the code for a function call emitFunction :: @@ -1144,9 +1144,9 @@ emitFunction rns _grpr _ _ _ (FReq r e) as = emitFunction _ _grpr _ _ ctx (FCont k) as | Just (i, BX) <- ctxResolve ctx k = Jump i as | Nothing <- ctxResolve ctx k = emitFunctionVErr k - | otherwise = internalBug $ "emitFunction: continuations are boxed" + | otherwise = internalBug [] $ "emitFunction: continuations are boxed" emitFunction _ _grpr _ _ _ (FPrim _) _ = - internalBug "emitFunction: impossible" + internalBug [] "emitFunction: impossible" countBlock :: Ctx v -> Int countBlock = go 0 @@ -1169,12 +1169,12 @@ matchCallingError cc b = "(" ++ show cc ++ "," ++ brs ++ ")" emitSectionVErr :: (Var v, HasCallStack) => v -> a emitSectionVErr v = - internalBug $ + internalBug [] $ "emitSection: could not resolve function variable: " ++ show v emitFunctionVErr :: (Var v, HasCallStack) => v -> a emitFunctionVErr v = - internalBug $ + internalBug [] $ "emitFunction: could not resolve function variable: " ++ show v -- Emit machine code for a let expression. Some expressions do not @@ -1211,7 +1211,7 @@ emitLet _ _ grpn _ _ _ ctx (TApp (FPrim p) args) = fmap (Ins . either emitPOp emitFOp p $ emitArgs grpn ctx args) emitLet rns grpr grpn rec d vcs ctx bnd | Direct <- d = - internalBug $ "unsupported compound direct let: " ++ show bnd + internalBug [] $ "unsupported compound direct let: " ++ show bnd | Indirect w <- d = \esect -> f @@ -1383,19 +1383,19 @@ emitPOp ANF.ANDB = emitP2 ANDB emitPOp ANF.IORB = emitP2 IORB emitPOp ANF.FORK = \case VArg1 i -> Fork i - _ -> internalBug "fork takes exactly one boxed argument" + _ -> internalBug [] "fork takes exactly one boxed argument" emitPOp ANF.ATOM = \case VArg1 i -> Atomically i - _ -> internalBug "atomically takes exactly one boxed argument" + _ -> internalBug [] "atomically takes exactly one boxed argument" emitPOp ANF.PRNT = \case VArg1 i -> Print i - _ -> internalBug "print takes exactly one boxed argument" + _ -> internalBug [] "print takes exactly one boxed argument" emitPOp ANF.INFO = \case ZArgs -> Info "debug" - _ -> internalBug "info takes no arguments" + _ -> internalBug [] "info takes no arguments" emitPOp ANF.TFRC = \case VArg1 i -> TryForce i - _ -> internalBug "tryEval takes exactly one boxed argument" + _ -> internalBug [] "tryEval takes exactly one boxed argument" -- handled in emitSection because Die is not an instruction @@ -1411,21 +1411,21 @@ emitFOp fop = ForeignCall True fop emitP1 :: Prim1 -> Args -> Instr emitP1 p (VArg1 i) = Prim1 p i emitP1 p a = - internalBug $ + internalBug [] $ "wrong number of args for unary unboxed primop: " ++ show (p, a) emitP2 :: Prim2 -> Args -> Instr emitP2 p (VArg2 i j) = Prim2 p i j emitP2 p a = - internalBug $ + internalBug [] $ "wrong number of args for binary unboxed primop: " ++ show (p, a) refCAS :: Args -> Instr refCAS (VArgN (primArrayToList -> [i, j, k])) = RefCAS i j k refCAS a = - internalBug $ + internalBug [] $ "wrong number of args for refCAS: " ++ show a @@ -1573,13 +1573,13 @@ emitClosures grpr grpn rec ctx args k = let cix = (CIx grpr grpn n) in Ins (Name (Env cix cix) ZArgs) <$> allocate (Var a BX ctx) as k | otherwise = - internalBug $ "emitClosures: unknown reference: " ++ show a ++ show grpr + internalBug [] $ "emitClosures: unknown reference: " ++ show a ++ show grpr emitArgs :: (Var v) => Word64 -> Ctx v -> [v] -> Args emitArgs grpn ctx args | Just l <- traverse (ctxResolve ctx) args = demuxArgs l | otherwise = - internalBug $ + internalBug [] $ "emitArgs[" ++ show grpn ++ "]: " diff --git a/unison-runtime/src/Unison/Runtime/Machine.hs b/unison-runtime/src/Unison/Runtime/Machine.hs index ef822309be..8572dbc7e9 100644 --- a/unison-runtime/src/Unison/Runtime/Machine.hs +++ b/unison-runtime/src/Unison/Runtime/Machine.hs @@ -66,7 +66,7 @@ import Unison.Runtime.ANF as ANF import Unison.Runtime.ANF qualified as ANF import Unison.Runtime.Array as PA import Unison.Runtime.Builtin hiding (unitValue) -import Unison.Runtime.Exception hiding (die) +import Unison.Runtime.Exception (RuntimeExn (BU, PE), die, peStr, prettyRuntimeExnSansCtx) import Unison.Runtime.Foreign import Unison.Runtime.Foreign.Function ( foreignCall, @@ -82,7 +82,6 @@ import Unison.Runtime.TypeTags qualified as TT import Unison.Symbol (Symbol) import Unison.Type qualified as Rf import Unison.Util.EnumContainers as EC -import Unison.Util.Pretty (toPlainUnbroken) import Unison.Util.Pretty qualified as P import Unison.Util.Text qualified as Util.Text import UnliftIO qualified @@ -152,7 +151,7 @@ apply0 !callback env !threadTracker !i = do topDEnv cmbs <$> readTVarIO (refTy env) <*> readTVarIO (refTm env) r <- case EC.lookup i cmbrs of Just r -> pure r - Nothing -> die "apply0: missing reference to entry point" + Nothing -> die [] "apply0: missing reference to entry point" let entryCix = (CIx r i 0) case unRComb $ rCombSection cmbs entryCix of Comb entryComb -> do @@ -243,7 +242,7 @@ exec _ !denv !_activeThreads !stk !k _ (Capture p) = do poke stk cap pure (False, denv, stk, k) exec env !denv !_activeThreads !stk !k _ (Prim1 CACH i) - | sandboxed env = die "attempted to use sandboxed operation: cache" + | sandboxed env = die [] "attempted to use sandboxed operation: cache" | otherwise = do arg <- peekOffS stk i news <- decodeCacheArgument arg @@ -254,7 +253,7 @@ exec env !denv !_activeThreads !stk !k _ (Prim1 CACH i) (Sq.fromList $ boxedVal . Foreign . Wrap Rf.termLinkRef . Ref <$> unknown) pure (False, denv, stk, k) exec env !denv !_activeThreads !stk !k _ (Prim1 LOAD i) - | sandboxed env = die "attempted to use sandboxed operation: load" + | sandboxed env = die [] "attempted to use sandboxed operation: load" | otherwise = do v <- peekOffBi stk i stk <- bumpn stk 2 @@ -283,7 +282,7 @@ exec _ !_ !_activeThreads !stk !k r (Prim2 THRO i j) = do () <- throwIO (BU (traceK r k) (Util.Text.toText name) x) error "throwIO should never return" exec env !denv !_activeThreads !stk !k _ (Prim2 TRCE i j) - | sandboxed env = die "attempted to use sandboxed operation: trace" + | sandboxed env = die [] "attempted to use sandboxed operation: trace" | otherwise = do tx <- peekOffBi stk i clo <- peekOff stk j @@ -305,7 +304,7 @@ exec env !denv !_trackThreads !stk !k _ (Prim2 op i j) = do stk <- primxx env stk op i j pure (False, denv, stk, k) exec env !denv !_activeThreads !stk !k _ (RefCAS refI ticketI valI) - | sandboxed env = die "attempted to use sandboxed operation: Ref.cas" + | sandboxed env = die [] "attempted to use sandboxed operation: Ref.cas" | otherwise = do (ref :: IORef Val) <- peekOffBi stk refI -- Note that the CAS machinery is extremely fussy w/r to whether things are forced because it @@ -344,21 +343,21 @@ exec _env !denv !_activeThreads !stk !k _ (ForeignCall _ func args) = do (b, stk) <- exStackIOToIO $ foreignCall func args (unpackXStack stk) pure (b, denv, stk, k) exec env !denv !activeThreads !stk !k _ (Fork i) - | sandboxed env = die "attempted to use sandboxed operation: fork" + | sandboxed env = die [] "attempted to use sandboxed operation: fork" | otherwise = do tid <- forkEval env activeThreads =<< peekOff stk i stk <- bump stk bpoke stk . Foreign . Wrap Rf.threadIdRef $ tid pure (False, denv, stk, k) exec env !denv !activeThreads !stk !k _ (Atomically i) - | sandboxed env = die $ "attempted to use sandboxed operation: atomically" + | sandboxed env = die [] $ "attempted to use sandboxed operation: atomically" | otherwise = do v <- peekOff stk i stk <- bump stk atomicEval env activeThreads (poke stk) v pure (False, denv, stk, k) exec env !denv !activeThreads !stk !k _ (TryForce i) - | sandboxed env = die $ "attempted to use sandboxed operation: tryForce" + | sandboxed env = die [] $ "attempted to use sandboxed operation: tryForce" | otherwise = do v <- peekOff stk i stk <- bump stk -- Bump the boxed stack to make a slot for the result, which will be written in the callback if we succeed. @@ -366,7 +365,7 @@ exec env !denv !activeThreads !stk !k _ (TryForce i) stk <- encodeExn stk ev pure (False, denv, stk, k) exec !_ !_ !_ !_ !_ _ (SandboxingFailure t) = do - die $ "Attempted to use disallowed builtin in sandboxed environment: " <> DTx.unpack t + die [] $ "Attempted to use disallowed builtin in sandboxed environment: " <> DTx.unpack t {-# INLINE exec #-} encodeExn :: @@ -386,17 +385,21 @@ encodeExn stk exc = do stk <- bumpn stk 3 pokeTag stk 0 bpokeOff stk 1 $ Foreign (Wrap Rf.typeLinkRef link) - pokeOffBi stk 2 msg + pokeOffBi stk 2 =<< msg stk <$ pokeOff stk 3 extra where - disp e = Util.Text.pack $ show e + disp :: (Exception e) => e -> IO Util.Text.Text + disp = pure . Util.Text.pack . show (link, msg, extra) | Just (ioe :: IOException) <- fromException exn = (Rf.ioFailureRef, disp ioe, unitValue) - | Just re <- fromException exn = case re of - PE _stk msg -> - (Rf.runtimeFailureRef, Util.Text.pack $ toPlainUnbroken msg, unitValue) - BU _ tx val -> (Rf.runtimeFailureRef, Util.Text.fromText tx, val) + | Just re <- fromException exn = + ( Rf.runtimeFailureRef, + Util.Text.pack . P.toPlain 0 <$> prettyRuntimeExnSansCtx re, + case re of + PE _ _ _ -> unitValue + BU _ _ val -> val + ) | Just (ae :: ArithException) <- fromException exn = (Rf.arithmeticFailureRef, disp ae, unitValue) | Just (nae :: NestedAtomically) <- fromException exn = @@ -489,14 +492,14 @@ eval env !denv !activeThreads !stk !k r (Ins i nx) = do unhandledAbilityRequest | otherwise -> eval env denv activeThreads stk k r nx eval _ !_ !_ !_activeThreads !_ _ Exit = pure () -eval _ !_ !_ !_activeThreads !_ _ (Die s) = die s +eval _ !_ !_ !_activeThreads !_ _ (Die s) = die [] s {-# NOINLINE eval #-} fakeCix :: CombIx fakeCix = CIx exceptionRef maxBound maxBound unhandledAbilityRequest :: (HasCallStack) => IO a -unhandledAbilityRequest = error . show . PE callStack . P.lit . fromString $ "eval: unhandled ability request" +unhandledAbilityRequest = error . displayException $ peStr [2922, 5400] "eval: unhandled ability request" forkEval :: CCache -> ActiveThreads -> Val -> IO ThreadId forkEval env activeThreads clo = @@ -569,7 +572,7 @@ name !stk !args = \case stk <- bump stk bpoke stk $ PAp cix comb seg pure stk - v -> die $ "naming non-function: " ++ show v + v -> die [] $ "naming non-function: " ++ show v {-# INLINE name #-} -- slow path application @@ -616,7 +619,7 @@ apply env !denv !activeThreads !stk !k !ck !args !val = stk <- bump stk poke stk v yield env denv activeThreads stk k - | otherwise = die $ "applying non-function: " ++ show v + | otherwise = die [] $ "applying non-function: " ++ show v {-# INLINE apply #-} jump :: @@ -636,7 +639,7 @@ jump env !denv !activeThreads !stk !k !args clo = case clo of stk <- dumpSeg stk seg $ F (countArgs args) a stk <- adjustArgs stk p repush env activeThreads stk denv sk k - _ -> die "jump: non-cont" + _ -> die [] "jump: non-cont" where -- Adjusts a repushed continuation to account for pending arguments. If -- there are any frames in the pushed continuation, the nearest one needs to @@ -669,7 +672,7 @@ repush env !activeThreads !stk = go cs' = EC.restrictKeys denv ps go !denv (Push n a cix f rsect sk) !k = go denv sk $ Push n a cix f rsect k - go !_ (CB _) !_ = die "repush: impossible" + go !_ (CB _) !_ = die [] "repush: impossible" {-# INLINE repush #-} moveArgs :: @@ -752,7 +755,7 @@ dumpDataValNoTag :: dumpDataValNoTag stk (BoxedVal c) = (closureTag c,) <$> dumpDataNoTag Nothing stk c dumpDataValNoTag _ v = - die $ "dumpDataValNoTag: unboxed val: " ++ show v + die [] $ "dumpDataValNoTag: unboxed val: " ++ show v {-# INLINE dumpDataValNoTag #-} -- Dumps a data type closure to the stack without writing its tag. @@ -776,7 +779,7 @@ dumpDataNoTag !mr !stk = \case stk <$ poke stk x DataG _ _ seg -> dumpSeg stk seg S clo -> - die $ + die [3320] $ "dumpDataNoTag: bad closure: " ++ show clo ++ maybe "" (\r -> "\nexpected type: " ++ show r) mr @@ -965,14 +968,14 @@ dumpBin sz k e l r stk = do dataBranchClosureError :: Maybe Reference -> Closure -> IO a dataBranchClosureError mrf clo = - die $ + die [] $ "dataBranch: bad closure: " ++ show clo ++ maybe "" (\r -> "\nexpected type: " ++ show r) mrf dataBranchBranchError :: MBranch -> IO a dataBranchBranchError br = - die $ "dataBranch: unexpected branch: " ++ show br + die [] $ "dataBranch: unexpected branch: " ++ show br -- Splits off a portion of the continuation up to a given prompt. -- @@ -998,9 +1001,9 @@ splitCont !denv !stk !k !p = asz = asize stk walk :: EnumMap Word64 Val -> SZ -> K -> K -> IO (Val, EnumMap Word64 Val, Stack, K) walk !denv !sz !ck KE = - die "fell off stack" >> finish denv sz 0 ck KE + die [] "fell off stack" >> finish denv sz 0 ck KE walk !denv !sz !ck (CB _) = - die "fell off stack" >> finish denv sz 0 ck KE + die [] "fell off stack" >> finish denv sz 0 ck KE walk !denv !sz !ck (Mark a ps cs k) | EC.member p ps = finish denv' sz a ck k | otherwise = walk denv' (sz + a) (Mark a ps cs' ck) k @@ -1034,7 +1037,7 @@ unhandledErr fname env i = Just r -> bomb (show r) Nothing -> bomb (show i) where - bomb sh = die $ fname ++ ": unhandled ability request: " ++ sh + bomb sh = die [] $ fname ++ ": unhandled ability request: " ++ sh rCombSection :: EnumMap Word64 MCombs -> CombIx -> MComb rCombSection combs (CIx r n i) = @@ -1064,8 +1067,8 @@ decodeCacheArgument s = for (toList s) $ \case (Val _unboxed (Data2 _ _ (BoxedVal (Foreign x)) (BoxedVal (Data2 _ _ (BoxedVal (Foreign y)) _)))) -> case unwrapForeign x of Ref r -> pure (r, unwrapForeign y) - _ -> die "decodeCacheArgument: Con reference" - _ -> die "decodeCacheArgument: unrecognized value" + _ -> die [] "decodeCacheArgument: Con reference" + _ -> die [] "decodeCacheArgument: unrecognized value" addRefs :: TVar Word64 -> @@ -1182,7 +1185,7 @@ preEvalTopLevelConstants cacheableCombs newCombs cc = do -- exceptions for top-level constant dependencies of docs and such, in -- case the docs don't actually evaluate them. isSandboxingException :: RuntimeExn -> Bool -isSandboxingException (PE _ (P.toPlainUnbroken -> msg)) = +isSandboxingException (PE _ _ (P.toPlain 0 -> msg)) = List.isPrefixOf sdbx1 msg || List.isPrefixOf sdbx2 msg where sdbx1 = "attempted to use sandboxed operation" @@ -1237,7 +1240,7 @@ reflectValue rty = goV refTy w | Just r <- EC.lookup w rty = pure r | otherwise = - die $ err "unknown type reference" + die [] $ err "unknown type reference" goIx (CIx r0 _ i) = ANF.GR r i where @@ -1266,10 +1269,10 @@ reflectValue rty = goV | Just m <- maybeUnwrapForeign Rf.hmapRef f -> goV . BoxedVal $ inflateMap m | otherwise -> ANF.BLit <$> goF f - BlackHole -> die $ err "black hole" - UnboxedTypeTag {} -> die $ err $ "unknown unboxed value" <> show val + BlackHole -> die [] $ err "black hole" + UnboxedTypeTag {} -> die [] . err $ "unknown unboxed value" <> show val - goK (CB _) = die $ err "callback continuation" + goK (CB _) = die [] $ err "callback continuation" goK KE = pure ANF.KE goK (Mark a ps de k) = do ps <- traverse refTy (EC.setToList ps) @@ -1301,7 +1304,7 @@ reflectValue rty = goV pure (ANF.BArr a) | Just a <- maybeUnwrapForeign Rf.iarrayRef f = ANF.Arr <$> traverse goV a - | otherwise = die $ err $ "foreign value: " <> (show f) + | otherwise = die [] . err $ "foreign value: " <> (show f) reifyValue :: CCache -> ANF.Value -> IO (Either [Reference] Val) reifyValue cc val = do @@ -1329,10 +1332,10 @@ reifyValue0 (combs, rty, rtm) = goV err s = "reifyValue: cannot restore value: " ++ s refTy r | Just w <- M.lookup r rty = pure w - | otherwise = die . err $ "unknown type reference: " ++ show r + | otherwise = die [] . err $ "unknown type reference: " ++ show r refTm r | Just w <- M.lookup r rtm = pure w - | otherwise = die . err $ "unknown term reference: " ++ show r + | otherwise = die [] . err $ "unknown term reference: " ++ show r goIx :: ANF.GroupRef -> IO (CombIx, MComb) goIx (ANF.GR r0 i) = refTm r <&> \n -> @@ -1347,7 +1350,7 @@ reifyValue0 (combs, rty, rtm) = goV (cix, RComb (Comb rcomb)) -> boxedVal . PApV cix rcomb <$> traverse goV vs (_, RComb (CachedVal _ val)) | [] <- vs -> pure val - | otherwise -> die . err $ msg + | otherwise -> die [] . err $ msg where msg = "reifyValue0: non-trivial partial application to cached value" goV (ANF.Data r t0 vs) = do @@ -1384,7 +1387,7 @@ reifyValue0 (combs, rty, rtm) = goV sect <$> goK k (CIx r _ _, _) -> - die . err $ + die [] . err $ "tried to reify a continuation with a cached value resumption" ++ show r diff --git a/unison-runtime/src/Unison/Runtime/Machine/Primops.hs b/unison-runtime/src/Unison/Runtime/Machine/Primops.hs index c34209a72b..8ae60741c4 100644 --- a/unison-runtime/src/Unison/Runtime/Machine/Primops.hs +++ b/unison-runtime/src/Unison/Runtime/Machine/Primops.hs @@ -15,6 +15,7 @@ import Unison.Prelude hiding (Text) import Unison.Reference (Reference) import Unison.Referent (Referent, toShortHash, pattern Ref) import Unison.Runtime.ANF (Code, Value, codeGroup) +import Unison.Runtime.Exception (die) import Unison.Runtime.Foreign import Unison.Runtime.Foreign.Function import Unison.Runtime.MCode @@ -450,7 +451,7 @@ refn stk v = do rrfc :: CCache -> Stack -> IORef Val -> IO () rrfc env stk ref - | sandboxed env = die "attempted to use sandboxed operation: Ref.readForCAS" + | sandboxed env = die [] "attempted to use sandboxed operation: Ref.readForCAS" | otherwise = do ticket <- Atomic.readForCAS ref pokeBi stk ticket @@ -460,12 +461,12 @@ tikr stk t = poke stk (Atomic.peekTicket t) miss :: CCache -> Stack -> Referent -> IO () miss env stk tl - | sandboxed env = die "attempted to use sandboxed operation: isMissing" + | sandboxed env = die [] "attempted to use sandboxed operation: isMissing" | otherwise = case tl of Ref link -> do m <- readTVarIO (intermed env) pokeBool stk (link `M.member` m) - _ -> die "exec:prim1:MISS: expected Ref" + _ -> die [] "exec:prim1:MISS: expected Ref" {-# INLINE miss #-} sdbl :: CCache -> Stack -> Referent -> IO () @@ -480,18 +481,18 @@ sandboxList _ _ = pure [] lkup :: CCache -> Stack -> Referent -> IO () lkup env stk tl - | sandboxed env = die "attempted to use sandboxed operation: lookup" + | sandboxed env = die [] "attempted to use sandboxed operation: lookup" | otherwise = writeBack stk =<< lookupCode env tl {-# INLINE lkup #-} cvld :: CCache -> Stack -> [(Referent, Code)] -> IO () cvld env stk news - | sandboxed env = die "attempted to use sandboxed operation: validate" + | sandboxed env = die [] "attempted to use sandboxed operation: validate" | otherwise = traverse extract news >>= codeValidate env >>= writeBack stk where extract (Ref r, code) = pure (r, codeGroup code) - extract _ = die "Prim1:CVLD: Con reference" + extract _ = die [] "Prim1:CVLD: Con reference" {-# INLINE cvld #-} tltt :: Stack -> Referent -> IO () @@ -502,7 +503,7 @@ tltt stk r = dbtx :: CCache -> Stack -> Val -> IO () dbtx env stk val | sandboxed env = - die "attempted to use sandboxed operation: Debug.toText" + die [] "attempted to use sandboxed operation: Debug.toText" | otherwise = writeBack stk traced where traced = case tracer env False val of @@ -877,7 +878,7 @@ iorb stk x y = pokeBool stk $ x || y sdbv :: CCache -> Stack -> [Referent] -> Value -> IO () sdbv env stk allowed0 v | sandboxed env = - die "attempted to use sandboxed operation: Value.validateSandboxed" + die [] "attempted to use sandboxed operation: Value.validateSandboxed" | otherwise = checkValueSandboxing env allowed v >>= writeBack stk where allowed = allowed0 >>= \case (Ref r) -> [r]; _ -> [] diff --git a/unison-runtime/src/Unison/Runtime/Machine/Types.hs b/unison-runtime/src/Unison/Runtime/Machine/Types.hs index d1d1a30109..f2db8855f7 100644 --- a/unison-runtime/src/Unison/Runtime/Machine/Types.hs +++ b/unison-runtime/src/Unison/Runtime/Machine/Types.hs @@ -1,4 +1,3 @@ - module Unison.Runtime.Machine.Types where import Control.Concurrent (ThreadId) @@ -8,21 +7,26 @@ import Data.IORef (IORef) import Data.Map.Strict qualified as M import Data.Set qualified as S import Data.Word -import GHC.Stack import Unison.Builtin.Decls (ioFailureRef) import Unison.Prelude import Unison.Reference (Reference, isBuiltin) import Unison.Referent (Referent, pattern Ref) import Unison.Runtime.ANF - (SuperGroup (..), Cacheability (..), Code (..), CompileExn (..), Value, valueLinks, foldGroupLinks) + ( Cacheability (..), + Code (..), + SuperGroup (..), + Value, + foldGroupLinks, + valueLinks, + ) import Unison.Runtime.Builtin -import Unison.Runtime.Exception hiding (die) +import Unison.Runtime.Exception qualified as Exception import Unison.Runtime.Foreign (Failure (..)) import Unison.Runtime.MCode import Unison.Runtime.Stack import Unison.Symbol import Unison.Util.EnumContainers as EC -import Unison.Util.Pretty qualified as P +import Unison.Util.Pretty qualified as Pretty import Unison.Util.Text as UText -- | A ref storing every currently active thread. @@ -64,18 +68,6 @@ refLookup s m r | otherwise = error $ "refLookup:" ++ s ++ ": unknown reference: " ++ show r -die :: (HasCallStack) => String -> IO a -die s = do - void . throwIO . PE callStack . P.lit . fromString $ s - -- This is unreachable, but we need it to fix some quirks in GHC's - -- worker/wrapper optimization, specifically, it seems that when throwIO's polymorphic return - -- value is specialized to a type like 'Stack' which we want GHC to unbox, it will sometimes - -- fail to unbox it, possibly because it can't unbox it when it's strictly a type application. - -- For whatever reason, this seems to fix it while still allowing us to throw exceptions in IO - -- like we prefer. - error "unreachable" -{-# INLINE die #-} - -- code caching environment data CCache = CCache { sandboxed :: Bool, @@ -105,7 +97,7 @@ refNumTm :: CCache -> Reference -> IO Word64 refNumTm cc r = refNumsTm cc >>= \case (M.lookup r -> Just w) -> pure w - _ -> die $ "refNumTm: unknown reference: " ++ show r + _ -> Exception.die [] $ "refNumTm: unknown reference: " ++ show r baseCCache :: Bool -> IO CCache baseCCache sandboxed = do @@ -143,11 +135,11 @@ baseCCache sandboxed = do lookupCode :: CCache -> Referent -> IO (Maybe Code) lookupCode env (Ref link) = - resolveCode link <$> - readTVarIO (intermed env) <*> - readTVarIO (refTm env) <*> - readTVarIO (cacheableCombs env) -lookupCode _ _ = die "lookupCode: Expected Ref" + resolveCode link + <$> readTVarIO (intermed env) + <*> readTVarIO (refTm env) + <*> readTVarIO (cacheableCombs env) +lookupCode _ _ = Exception.die [] "lookupCode: Expected Ref" resolveCode :: Reference -> @@ -235,7 +227,7 @@ codeValidate cc tml = do rns = RN (refLookup "ty" rty) (refLookup "tm" rtm) (const Nothing) combinate (n, (r, g)) = evaluate $ emitCombs rns r n g (Nothing <$ traverse_ combinate (zip [ftm ..] tml)) - `catch` \(CE cs perr) -> - let msg = UText.pack $ P.toPlainUnbroken perr - extra = UText.pack $ show cs - in pure . Just $ Failure ioFailureRef msg extra + `catch` \ce@(Exception.CE cs _ _) -> do + msg <- fmap (UText.pack . Pretty.toPlain 0) $ Exception.prettyCompileExn ce + let extra = UText.pack $ show cs + pure . Just $ Failure ioFailureRef msg extra diff --git a/unison-runtime/src/Unison/Runtime/Pattern.hs b/unison-runtime/src/Unison/Runtime/Pattern.hs index 21999727a3..8adfb0b217 100644 --- a/unison-runtime/src/Unison/Runtime/Pattern.hs +++ b/unison-runtime/src/Unison/Runtime/Pattern.hs @@ -36,7 +36,7 @@ import Unison.Pattern import Unison.Pattern qualified as P import Unison.Prelude hiding (guard) import Unison.Reference (Reference, Reference' (Builtin, DerivedId)) -import Unison.Runtime.ANF (internalBug) +import Unison.Runtime.InternalError (internalBug) import Unison.Term hiding (Term, matchPattern) import Unison.Term qualified as Tm import Unison.Type qualified as Rf @@ -62,7 +62,7 @@ instance Semigroup PType where t@(PData l) <> PData r | l == r = t PReq l <> PReq r = PReq (l <> r) - _ <> _ = internalBug "inconsistent pattern matching types" + _ <> _ = internalBug [] "inconsistent pattern matching types" instance Monoid PType where mempty = Unknown @@ -93,7 +93,7 @@ builtinDataSpec = Map.fromList decls | (_, x, y) <- builtinEffectDecls ] -findPattern :: Eq v => v -> PatternRow v -> Maybe (Pattern v) +findPattern :: (Eq v) => v -> PatternRow v -> Maybe (Pattern v) findPattern v (PR ms _ _) | (_, p : _) <- break ((== v) . loc) ms = Just p | otherwise = Nothing @@ -121,7 +121,7 @@ type Heuristic v = PatternMatrix v -> Maybe v choose :: [Heuristic v] -> PatternMatrix v -> v choose [] (PM (PR (p : _) _ _ : _)) = loc p choose [] _ = - internalBug "pattern matching: failed to choose a splitting" + internalBug [] "pattern matching: failed to choose a splitting" choose (h : hs) m | Just i <- h m = i | otherwise = choose hs m @@ -178,7 +178,7 @@ decomposePattern (Just rf0) t nfields p@(P.Constructor _ (ConstructorReference r rf0 == rf = if length ps == nfields then [ps] - else internalBug err + else internalBug [] err where err = "decomposePattern: wrong number of constructor fields: " @@ -188,7 +188,7 @@ decomposePattern (Just rf0) t nfields p@(P.EffectBind _ (ConstructorReference rf rf0 == rf = if length ps + 1 == nfields then [ps ++ [pk]] - else internalBug err + else internalBug [] err where err = "decomposePattern: wrong number of ability fields: " @@ -200,7 +200,7 @@ decomposePattern _ _ nfields (P.Var _) = decomposePattern _ _ nfields (P.Unbound _) = [replicate nfields (P.Unbound (typed Pattern))] decomposePattern _ _ _ (P.SequenceLiteral _ _) = - internalBug "decomposePattern: sequence literal" + internalBug [] "decomposePattern: sequence literal" decomposePattern _ _ _ _ = [] matchBuiltin :: P.Pattern a -> Maybe (P.Pattern ()) @@ -251,7 +251,7 @@ decideSeqPat = go False go b (P.Unbound _ : ps) = go b ps go b (P.Var _ : ps) = go b ps go _ (p : _) = - internalBug $ "Cannot process sequence pattern: " ++ show p + internalBug [] $ "Cannot process sequence pattern: " ++ show p -- Represents the possible correspondences between a sequence pattern -- and a sequence matching compilation target. Unlike data matching, @@ -501,7 +501,7 @@ antiSplitMatrix :: antiSplitMatrix v (PM rs) = PM (f =<< rs) where -- keep rows that do not have a refutable pattern for v - f r = [ r | isNothing $ findPattern v r ] + f r = [r | isNothing $ findPattern v r] -- Monad for pattern preparation. It is a state monad carrying a fresh -- variable source, the list of variables bound the pattern being @@ -519,12 +519,7 @@ useVar = state $ \case _ -> error "useVar: Expected multiple vars" renameTo :: (Var v) => v -> v -> PPM v () -renameTo to from = - modify $ \(avoid, vs, rn) -> - ( avoid, - vs, - insertWith (internalBug "renameTo: duplicate rename") from to rn - ) +renameTo to from = modify . fmap $ insertWith (internalBug [3625, 4463] "renameTo: duplicate rename") from to -- Tries to rewrite sequence patterns into a format that can be -- matched most flexibly. @@ -586,7 +581,7 @@ preparePattern p = prepareAs p =<< freshVar buildPattern :: Bool -> ConstructorReference -> [v] -> Int -> P.Pattern () buildPattern effect r vs nfields - | effect, [] <- vps = internalBug "too few patterns for effect bind" + | effect, [] <- vps = internalBug [] "too few patterns for effect bind" | effect = P.EffectBind () r (init vps) (last vps) | otherwise = P.Constructor () r vps where @@ -636,12 +631,13 @@ compile spec ctx m@(PM (r : rs)) case lookupData rf spec of Right cons -> match () (var () v) $ - (buildCase spec rf False cons ctx - <$> splitMatrix v (Just rf) ncons m) + ( buildCase spec rf False cons ctx + <$> splitMatrix v (Just rf) ncons m + ) ++ buildDefaultCase spec False needDefault ctx dm where needDefault = length ncons < length cons - Left err -> internalBug err + Left err -> internalBug [] err | PReq rfs <- ty = match () (var () v) $ [ buildCasePure spec ctx tup @@ -653,7 +649,7 @@ compile spec ctx m@(PM (r : rs)) tup <- splitMatrix v (Just rf) (numberCons cons) m ] | Unknown <- ty = - internalBug "unknown pattern compilation type" + internalBug [] "unknown pattern compilation type" where v = choose heuristics m ncons = relevantConstructors m v @@ -663,7 +659,7 @@ compile spec ctx m@(PM (r : rs)) -- Calculates the data constructors—with their arities—that should be -- matched on when splitting a matrix on a given variable. This -- includes -relevantConstructors :: Ord v => PatternMatrix v -> v -> [(Int, Int)] +relevantConstructors :: (Ord v) => PatternMatrix v -> v -> [(Int, Int)] relevantConstructors (PM rows) v = search [] rows where search acc (row : rows) @@ -673,7 +669,7 @@ relevantConstructors (PM rows) v = search [] rows Just (P.Boolean _ b) -> search ((if b then 1 else 0, 0) : acc) rows Just p -> - internalBug $ "unexpected data pattern: " ++ show p + internalBug [] $ "unexpected data pattern: " ++ show p -- if the pattern is not found, it must have been irrefutable, -- so contributes no relevant constructor. _ -> search acc rows @@ -749,7 +745,7 @@ mkRow sv (MatchCase (normalizeSeqP -> p0) g0 (AbsN' vs b)) = (filter refutable [p]) (renames rn <$> g) (renames rn b) - _ -> internalBug "mkRow: not all variables used" + _ -> internalBug [] "mkRow: not all variables used" where g = case g0 of Just (AbsN' us g) @@ -757,7 +753,7 @@ mkRow sv (MatchCase (normalizeSeqP -> p0) g0 (AbsN' vs b)) = | length us == length vs -> Just $ renames (Map.fromList (zip us vs)) g | otherwise -> - internalBug "mkRow: guard variables do not match body" + internalBug [] "mkRow: guard variables do not match body" Nothing -> Nothing initialize :: @@ -779,7 +775,7 @@ initialize r sc cs = do pv = freshenId n $ typed Pattern grabId :: State Word64 Word64 -grabId = state $ \n -> (n, n+1) +grabId = state $ \n -> (n, n + 1) splitPatterns :: (Var v) => DataSpec -> Term v -> Term v splitPatterns spec0 tm = evalState (splitPatterns0 spec tm) 0 diff --git a/unison-runtime/src/Unison/Runtime/Serialize.hs b/unison-runtime/src/Unison/Runtime/Serialize.hs index 29c8a8b89f..0c85acf375 100644 --- a/unison-runtime/src/Unison/Runtime/Serialize.hs +++ b/unison-runtime/src/Unison/Runtime/Serialize.hs @@ -26,7 +26,7 @@ import Unison.Hash qualified as Hash import Unison.Reference (Id' (..), Reference, Reference' (Builtin, DerivedId), pattern Derived) import Unison.Referent (Referent, pattern Con, pattern Ref) import Unison.Runtime.Array qualified as PA -import Unison.Runtime.Exception +import Unison.Runtime.Exception (exn) import Unison.Runtime.MCode ( Prim1 (..), Prim2 (..), @@ -37,7 +37,7 @@ import Unison.Util.EnumContainers as EC unknownTag :: (MonadGet m) => String -> Word8 -> m a unknownTag t w = remaining >>= \r -> - exn $ + exn [] $ "unknown " ++ t ++ " word: " @@ -77,7 +77,7 @@ getBool = d =<< getWord8 where d 0 = pure False d 1 = pure True - d n = exn $ "getBool: bad tag: " ++ show n + d n = exn [] $ "getBool: bad tag: " ++ show n putNat :: (MonadPut m) => Word64 -> m () putNat = putWord64be @@ -119,7 +119,7 @@ putPositive :: n -> m () putPositive n - | n < 0 = exn $ "putPositive: negative number: " ++ show (toInteger n) + | n < 0 = exn [] $ "putPositive: negative number: " ++ show (toInteger n) | otherwise = serialize (VarInt n) -- Reads as an Integer, then checks that the result will fit in the @@ -328,7 +328,6 @@ instance Tag Prim1 where tag2word RNDF = 32 tag2word TRNC = 33 tag2word NOTB = 34 - tag2word SIZT = 35 tag2word USNC = 36 tag2word UCNS = 37 @@ -396,7 +395,6 @@ instance Tag Prim1 where word2tag 32 = pure RNDF word2tag 33 = pure TRNC word2tag 34 = pure NOTB - word2tag 35 = pure SIZT word2tag 36 = pure USNC word2tag 37 = pure UCNS @@ -555,7 +553,6 @@ instance Tag Prim2 where word2tag 44 = pure DRPN word2tag 45 = pure ANDB word2tag 46 = pure IORB - word2tag 47 = pure EQLU word2tag 48 = pure LEQU word2tag 49 = pure LESU @@ -586,4 +583,3 @@ instance Tag Prim2 where word2tag 74 = pure SDBV word2tag 75 = pure REFW word2tag n = unknownTag "Prim2" n - diff --git a/unison-runtime/src/Unison/Runtime/Stack.hs b/unison-runtime/src/Unison/Runtime/Stack.hs index a0338efcc7..d1f1459d9b 100644 --- a/unison-runtime/src/Unison/Runtime/Stack.hs +++ b/unison-runtime/src/Unison/Runtime/Stack.hs @@ -748,12 +748,15 @@ instance Show Stack where type UVal = Int -- | A runtime value, which is either a boxed or unboxed value, but we may not know which. +-- +-- __TODO__: Can this be represented with `These` instead, distinguishing the case where we don’t know which (`These`) +-- from the known unboxed (`This`) and known boxed (`That`) cases? Or is it the case that we /do not/ know +-- which? data Val = Val {getUnboxedVal :: !UVal, getBoxedVal :: !BVal} - -- The Eq instance for Val is deliberately omitted because you need to take into account the fact that if a Val is boxed, the - -- unboxed side is garbage and should not be compared. - -- See universalEq. deriving (Show) +-- | The `Eq` instance for `Val` can’t be derived because you need to take into account the fact that if a `Val` is +-- boxed, the unboxed side is garbage and should not be compared. instance Eq Val where (==) = universalEq (==) diff --git a/unison-runtime/src/Unison/Runtime/TypeTags.hs b/unison-runtime/src/Unison/Runtime/TypeTags.hs index 3a15b91f7a..fd2fa3ecfd 100644 --- a/unison-runtime/src/Unison/Runtime/TypeTags.hs +++ b/unison-runtime/src/Unison/Runtime/TypeTags.hs @@ -45,30 +45,19 @@ module Unison.Runtime.TypeTags ) where -import Control.Exception (throw) import Data.Bits (shiftL, shiftR, (.&.), (.|.)) import Data.List hiding (and, or) import Data.Map qualified as Map -import GHC.Stack (CallStack, callStack) import U.Codebase.Reference (Reference) import Unison.Builtin.Decls qualified as Ty import Unison.Prelude import Unison.Runtime.Builtin.Types (builtinTypeNumbering) +import Unison.Runtime.InternalError (internalBug) import Unison.Type qualified as Ty import Unison.Util.EnumContainers as EC -import Unison.Util.Pretty qualified as Pretty import Prelude hiding (abs, and, or, seq) import Prelude qualified --- For internal errors -data CompileExn = CE CallStack (Pretty.Pretty Pretty.ColorText) - deriving (Show) - -instance Exception CompileExn - -internalBug :: (HasCallStack) => String -> a -internalBug = throw . CE callStack . Pretty.lit . fromString - -- Types representing components that will go into the runtime tag of -- a data type value. RTags correspond to references, while CTags -- correspond to constructors. @@ -107,13 +96,13 @@ maskTags (PackedTag w) = (w .&. 0xFFFF) ensureRTag :: (Ord n, Show n, Num n) => String -> n -> r -> r ensureRTag s n x | n > 0xFFFFFFFFFFFF = - internalBug $ s ++ "@RTag: too large: " ++ show n + internalBug [] $ s ++ "@RTag: too large: " ++ show n | otherwise = x ensureCTag :: (Ord n, Show n, Num n) => String -> n -> r -> r ensureCTag s n x | n > 0xFFFF = - internalBug $ s ++ "@CTag: too large: " ++ show n + internalBug [] $ s ++ "@CTag: too large: " ++ show n | otherwise = x instance Enum RTag where @@ -126,19 +115,19 @@ instance Enum CTag where instance Num RTag where fromInteger i = ensureRTag "fromInteger" i . RTag $ fromInteger i - (+) = internalBug "RTag: +" - (*) = internalBug "RTag: *" - abs = internalBug "RTag: abs" - signum = internalBug "RTag: signum" - negate = internalBug "RTag: negate" + (+) = internalBug [] "RTag: +" + (*) = internalBug [] "RTag: *" + abs = internalBug [] "RTag: abs" + signum = internalBug [] "RTag: signum" + negate = internalBug [] "RTag: negate" instance Num CTag where fromInteger i = ensureCTag "fromInteger" i . CTag $ fromInteger i - (+) = internalBug "CTag: +" - (*) = internalBug "CTag: *" - abs = internalBug "CTag: abs" - signum = internalBug "CTag: signum" - negate = internalBug "CTag: negate" + (+) = internalBug [] "CTag: +" + (*) = internalBug [] "CTag: *" + abs = internalBug [] "CTag: abs" + signum = internalBug [] "CTag: signum" + negate = internalBug [] "CTag: negate" floatTag :: PackedTag floatTag = mkSimpleTag "floatTag" Ty.floatRef @@ -170,54 +159,76 @@ failureTag = mkEnumTag "failureTag" Ty.failureRef 0 noneTag, someTag :: PackedTag (noneTag, someTag) | [nt, st] <- - mkTags "optional tags" Ty.optionalRef - [Ty.noneId, Ty.someId] = (nt, st) - | otherwise = error "internal error: optional tags" + mkTags + "optional tags" + Ty.optionalRef + [Ty.noneId, Ty.someId] = + (nt, st) + | otherwise = internalBug [] "missing optional tags" leftTag, rightTag :: PackedTag (leftTag, rightTag) | [lt, rt] <- - mkTags "either tags" Ty.eitherRef - [Ty.eitherLeftId, Ty.eitherRightId] = (lt, rt) - | otherwise = error "internal error: either tags" + mkTags + "either tags" + Ty.eitherRef + [Ty.eitherLeftId, Ty.eitherRightId] = + (lt, rt) + | otherwise = internalBug [] "missing either tags" noBufTag, lineBufTag, blockBufTag, sizedBlockBufTag :: PackedTag (noBufTag, lineBufTag, blockBufTag, sizedBlockBufTag) - | [nt,lt,bt,st] <- - mkTags "buffer mode tags" Ty.bufferModeRef + | [nt, lt, bt, st] <- + mkTags + "buffer mode tags" + Ty.bufferModeRef [ Ty.bufferModeNoBufferingId, Ty.bufferModeLineBufferingId, Ty.bufferModeBlockBufferingId, - Ty.bufferModeSizedBlockBufferingId ] = (nt, lt, bt, st) - | otherwise = error "internal error: buffer mode tags" + Ty.bufferModeSizedBlockBufferingId + ] = + (nt, lt, bt, st) + | otherwise = internalBug [] "missing buffer mode tags" readModeTag, writeModeTag, appendModeTag, readWriteModeTag :: PackedTag (readModeTag, writeModeTag, appendModeTag, readWriteModeTag) - | [rt,wt,at,rwt] <- - mkTags "file mode tags" Ty.fileModeRef + | [rt, wt, at, rwt] <- + mkTags + "file mode tags" + Ty.fileModeRef [ Ty.fileModeReadId, Ty.fileModeWriteId, Ty.fileModeAppendId, - Ty.fileModeReadWriteId ] = (rt, wt, at, rwt) - | otherwise = error "internal error: file mode tags" + Ty.fileModeReadWriteId + ] = + (rt, wt, at, rwt) + | otherwise = internalBug [] "missing file mode tags" seekAbsoluteTag, seekRelativeTag, seekEndTag :: PackedTag (seekAbsoluteTag, seekRelativeTag, seekEndTag) | [at, rt, et] <- - mkTags "seek mode tags" Ty.seekModeRef + mkTags + "seek mode tags" + Ty.seekModeRef [ Ty.seekModeAbsoluteId, Ty.seekModeRelativeId, - Ty.seekModeEndId ] = (at, rt, et) - | otherwise = error "internal error: seek mode tags" + Ty.seekModeEndId + ] = + (at, rt, et) + | otherwise = internalBug [] "missing seek mode tags" stdInTag, stdOutTag, stdErrTag :: PackedTag (stdInTag, stdOutTag, stdErrTag) | [it, ot, et] <- - mkTags "standard handle tags" Ty.stdHandleRef + mkTags + "standard handle tags" + Ty.stdHandleRef [ Ty.stdInId, Ty.stdOutId, - Ty.stdErrId ] = (it, ot, et) - | otherwise = error "internal error: standard handle tags" + Ty.stdErrId + ] = + (it, ot, et) + | otherwise = internalBug [] "missing standard handle tags" exceptionTag :: Word64 exceptionRaiseTag :: PackedTag @@ -226,38 +237,48 @@ exceptionRaiseTag :: PackedTag et <- toEnum $ fromIntegral n, rt <- toEnum $ fromIntegral Ty.exceptionRaiseId = (n, packTags et rt) - | otherwise = internalBug $ "internal error: Exception tag" + | otherwise = internalBug [] "missing Exception tag" pairTag :: PackedTag pairTag | Just n <- Map.lookup Ty.pairRef builtinTypeNumbering, pt <- toEnum (fromIntegral n) = packTags pt 0 - | otherwise = internalBug "internal error: pairTag" + | otherwise = internalBug [] "missing pairTag" seqViewEmptyTag, seqViewElemTag :: PackedTag (seqViewEmptyTag, seqViewElemTag) | [emt, elt] <- - mkTags "seq view tags" Ty.seqViewRef + mkTags + "seq view tags" + Ty.seqViewRef [ Ty.seqViewEmpty, - Ty.seqViewElem ] = (emt, elt) - | otherwise = error "internal error: seq view tags" + Ty.seqViewElem + ] = + (emt, elt) + | otherwise = internalBug [] "missing seq view tags" mapTipTag, mapBinTag :: PackedTag (mapTipTag, mapBinTag) | [mtt, mbt] <- - mkTags "map tags" Ty.mapRef + mkTags + "map tags" + Ty.mapRef [ Ty.mapTip, Ty.mapBin - ] = (mtt, mbt) - | otherwise = error "internal error: map tags" + ] = + (mtt, mbt) + | otherwise = internalBug [] "missing map tags" setWrapTag :: PackedTag setWrapTag | [swt] <- - mkTags "set tag" Ty.setRef - [ Ty.setWrap ] = swt - | otherwise = error "internal error: set tag" + mkTags + "set tag" + Ty.setRef + [Ty.setWrap] = + swt + | otherwise = internalBug [] "missing set tag" -- | A tag we use to represent the 'pure' effect case. pureEffectTag :: PackedTag @@ -272,11 +293,11 @@ mkEnumTag msg r i | Just n <- Map.lookup r builtinTypeNumbering, rt <- toEnum (fromIntegral n) = packTags rt (toEnum i) - | otherwise = internalBug $ "internal error: " <> msg + | otherwise = internalBug [] $ "missing enum tag: " <> msg mkTags :: String -> Reference -> [Word64] -> [PackedTag] mkTags msg r cs | Just n <- Map.lookup r builtinTypeNumbering, tt <- toEnum $ fromIntegral n = packTags tt . toEnum . fromIntegral <$> cs - | otherwise = error $ "internal error: " ++ msg + | otherwise = internalBug [] $ "missing tag: " ++ msg diff --git a/unison-runtime/unison-runtime.cabal b/unison-runtime/unison-runtime.cabal index d730b8a8bf..551dc19c0c 100644 --- a/unison-runtime/unison-runtime.cabal +++ b/unison-runtime/unison-runtime.cabal @@ -50,6 +50,7 @@ library Unison.Runtime.Foreign.Function Unison.Runtime.Foreign.Function.Type Unison.Runtime.Interface + Unison.Runtime.InternalError Unison.Runtime.IOSource Unison.Runtime.Machine Unison.Runtime.Machine.Primops @@ -116,6 +117,7 @@ library , directory , exceptions , filepath + , github , inspection-testing , iproute , lens diff --git a/unison-share-api/src/Unison/Server/Backend.hs b/unison-share-api/src/Unison/Server/Backend.hs index c5130c6d3e..2653d01d56 100644 --- a/unison-share-api/src/Unison/Server/Backend.hs +++ b/unison-share-api/src/Unison/Server/Backend.hs @@ -66,6 +66,7 @@ module Unison.Server.Backend -- * Re-exported for Share Server termsToSyntax, termsToSyntaxOf, + typeToSyntax, typesToSyntax, typesToSyntaxOf, definitionResultsDependencies, @@ -526,10 +527,7 @@ formatTypeName ppe = fmap Syntax.convertElement . formatTypeName' ppe formatTypeName' :: PPE.PrettyPrintEnv -> Reference -> SyntaxText -formatTypeName' ppe r = - Pretty.renderUnbroken - . NP.styleHashQualified id - $ PPE.typeName ppe r +formatTypeName' ppe = Pretty.render 0 . NP.styleHashQualified id . PPE.typeName ppe termEntryToNamedTerm :: (Var v) => PPE.PrettyPrintEnv -> Maybe Width -> TermEntry v a -> NamedTerm @@ -1131,15 +1129,12 @@ termsToSyntax suff width ppe0 terms = terms <&> \(r, dispObj) -> let n = PPE.termName ppeDecl . Referent.Ref $ r - in (r,) case dispObj of - DisplayObject.BuiltinObject typ -> - DisplayObject.BuiltinObject $ - formatType' (ppeBody r) width typ - DisplayObject.MissingObject sh -> DisplayObject.MissingObject sh - DisplayObject.UserObject tm -> - DisplayObject.UserObject - . Pretty.render width - $ TermPrinter.prettyBinding (ppeBody r) n tm + in ( r, + bimap + (formatType' (ppeBody r) width) + (Pretty.render width . TermPrinter.prettyBinding (ppeBody r) n) + dispObj + ) where ppeBody r = if suffixified suff @@ -1176,23 +1171,29 @@ typesToSyntaxOf suff width ppe0 trav s = -- | Converts Type Display Objects into Syntax Text. typesToSyntax :: - (Var v) => - (Ord a) => + (Var v, Ord a) => Suffixify -> Width -> PPED.PrettyPrintEnvDecl -> [(TypeReference, (DisplayObject () (DD.Decl v a)))] -> [(TypeReference, (DisplayObject SyntaxText SyntaxText))] -typesToSyntax suff width ppe0 types = - types - <&> \(r, dispObj) -> - let n = PPE.typeName ppeDecl r - in (r,) $ case dispObj of - BuiltinObject _ -> BuiltinObject (formatTypeName' ppeDecl r) - MissingObject sh -> MissingObject sh - UserObject d -> - UserObject . Pretty.render width $ - DeclPrinter.prettyDecl ppe0 DeclPrinter.RenderUniqueTypeGuids'No r n d +typesToSyntax suff width ppe0 = + fmap \(r, dispObj) -> (r, typeToSyntax suff width ppe0 r dispObj) + +-- | Converts a Type Display Object into Syntax Text. +typeToSyntax :: + (Var v, Ord a) => + Suffixify -> + Width -> + PPED.PrettyPrintEnvDecl -> + TypeReference -> + DisplayObject () (DD.Decl v a) -> + DisplayObject SyntaxText SyntaxText +typeToSyntax suff width ppe0 r = + let n = PPE.typeName ppeDecl r + in bimap + (\() -> formatTypeName' ppeDecl r) + (Pretty.render width . DeclPrinter.prettyDecl ppe0 DeclPrinter.RenderUniqueTypeGuids'No r n) where ppeDecl = if suffixified suff @@ -1213,15 +1214,10 @@ typeToSyntaxHeader :: HQ.HashQualified Name -> DisplayObject () (DD.Decl Symbol Ann) -> DisplayObject SyntaxText SyntaxText -typeToSyntaxHeader width hqName obj = - case obj of - BuiltinObject _ -> - let syntaxName = Pretty.renderUnbroken . NP.styleHashQualified id $ hqName - in BuiltinObject syntaxName - MissingObject sh -> MissingObject sh - UserObject d -> - UserObject . Pretty.render width $ - DeclPrinter.prettyDeclHeader DeclPrinter.RenderUniqueTypeGuids'No hqName d +typeToSyntaxHeader width hqName = + bimap + (\() -> Pretty.render 0 $ NP.styleHashQualified id hqName) + (Pretty.render width . DeclPrinter.prettyDeclHeader DeclPrinter.RenderUniqueTypeGuids'No hqName) loadSearchResults :: Codebase m Symbol Ann -> diff --git a/unison-src/builtin-tests/interpreter-tests.output.md b/unison-src/builtin-tests/interpreter-tests.output.md index dacb9d3315..510db1f36a 100644 --- a/unison-src/builtin-tests/interpreter-tests.output.md +++ b/unison-src/builtin-tests/interpreter-tests.output.md @@ -13,7 +13,7 @@ scratch/main> delete.project runtime-tests ``` ``` ucm :hide -scratch/main> clone @unison/runtime-tests/releases/0.0.3 runtime-tests/selected +scratch/main> clone @unison/runtime-tests/@sellout/unison-5661 runtime-tests/selected ``` ``` ucm diff --git a/unison-src/builtin-tests/interpreter-tests.sh b/unison-src/builtin-tests/interpreter-tests.sh index 743c1f9651..04c4f09ce4 100755 --- a/unison-src/builtin-tests/interpreter-tests.sh +++ b/unison-src/builtin-tests/interpreter-tests.sh @@ -7,7 +7,7 @@ else ucm="$1" fi -runtime_tests_version="@unison/runtime-tests/releases/0.0.3" +runtime_tests_version="@unison/runtime-tests/@sellout/unison-5661" codebase=${XDG_CACHE_HOME:-"$HOME/.cache"}/unisonlanguage/runtime-tests.unison diff --git a/unison-src/transcripts-using-base/failure-tests.output.md b/unison-src/transcripts-using-base/failure-tests.output.md index 5087b2d934..49b5e6621e 100644 --- a/unison-src/transcripts-using-base/failure-tests.output.md +++ b/unison-src/transcripts-using-base/failure-tests.output.md @@ -63,7 +63,10 @@ scratch/main> io.test test2 The program halted with an unhandled exception: - Failure (typeLink RuntimeFailure) "builtin.bug" (Any "whoa") + Failure + (typeLink RuntimeFailure) + "💔💥\n\nI've encountered a call to builtin.bug with the following value:\n\n \"whoa\"\n\nStack trace:\n ##bug" + (Any "whoa") Stack trace: ##raise diff --git a/unison-src/transcripts/idempotent/fix-4463.md b/unison-src/transcripts/idempotent/fix-4463.md new file mode 100644 index 0000000000..d056ad80c9 --- /dev/null +++ b/unison-src/transcripts/idempotent/fix-4463.md @@ -0,0 +1,58 @@ +``` ucm :hide +myproj/main> builtins.mergeio +``` + +Repeated variable names in a pattern match (like `p` below), should fail to parse, but doesn’t. + +``` unison :error :bug +first : (Text, Text) -> Text +first = cases (p, p) -> p + +main: '{IO, Exception} Text +main = do first ("one", "two") +``` + +``` ucm :added-by-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`: + + first : (Text, Text) -> Text + main : '{IO, Exception} Text +``` + +``` ucm +scratch/main> add + + ⍟ I've added these definitions: + + first : (##Text, ##Text) -> ##Text + main : '{##IO, #4n0fgs00hp} ##Text +``` + +Instead, we get a runtime error when we try to run the function. + +``` ucm :bug +scratch/main> run main + + ❗️ + + Sorry – I've encountered a bug in the Unison runtime. + + renameTo: duplicate rename + + Please check if one of these known issues matches your + situation: + + * https://github.com/unisonweb/unison/issues/3625 + * `renameTo: duplicate rename` pattern matching bug prevents + ability to add/update functions + https://github.com/unisonweb/unison/issues/4463 + + If not, please open a new one: + https://github.com/unisonweb/unison/issues/new/choose +```