Commit 94048f9f authored by Eric Seidel's avatar Eric Seidel Committed by Ben Gamari
Browse files

Hide the CallStack implicit parameter

The implicit parameter isn't actually very relevant to the CallStack
machinery, so we hide the implementation details behind a constraint
alias

```
type HasCallStack = (?callStack :: CallStack)
```

This has a few benefits:

1. No need to enable `ImplicitParams` in user code.
2. No need to remember the `?callStack` naming convention.
3. Gives us the option to change the implementation details in the
future with less user-land breakage.

The revised `CallStack` API is exported from `GHC.Stack` and makes no
mention of the implicit parameter.

Test Plan: ./validate

Reviewers: simonpj, austin, hvr, bgamari

Reviewed By: simonpj, bgamari

Subscribers: thomie

Projects: #ghc

Differential Revision: https://phabricator.haskell.org/D1818
parent 0d601657
......@@ -527,16 +527,18 @@ Note [Overview of implicit CallStacks]
The goal of CallStack evidence terms is to reify locations
in the program source as runtime values, without any support
from the RTS. We accomplish this by assigning a special meaning
to implicit parameters of type GHC.Stack.CallStack.
to constraints of type GHC.Stack.Types.HasCallStack, an alias
Implicit CallStacks are regular implicit parameters, augmented with two
extra rules in the constraint solver:
type HasCallStack = (?callStack :: CallStack)
Implicit parameters of type GHC.Stack.Types.CallStack (the name is not
important) are solved in three steps:
1. Occurrences of CallStack IPs are solved directly from the given IP,
just like a regular IP. For example, the occurrence of `?stk` in
error :: (?stk :: CallStack) => String -> a
error s = raise (ErrorCall (s ++ show ?stk))
error s = raise (ErrorCall (s ++ prettyCallStack ?stk))
will be solved for the `?stk` in `error`s context as before.
......@@ -604,14 +606,15 @@ in `g`, because `head` did not explicitly request a CallStack.
Important Details:
- GHC should NEVER report an insoluble CallStack constraint.
- A CallStack (defined in GHC.Stack) is a [(String, SrcLoc)], where the String
is the name of the binder that is used at the SrcLoc. SrcLoc is defined in
GHC.SrcLoc and contains the package/module/file name, as well as the full
source-span. Both CallStack and SrcLoc are kept abstract so only GHC can
construct new values.
- A CallStack (defined in GHC.Stack.Types) is a [(String, SrcLoc)],
where the String is the name of the binder that is used at the
SrcLoc. SrcLoc is also defined in GHC.Stack.Types and contains the
package/module/file name, as well as the full source-span. Both
CallStack and SrcLoc are kept abstract so only GHC can construct new
values.
- We will automatically solve any wanted CallStack regardless of the name of the
IP, i.e.
- We will automatically solve any wanted CallStack regardless of the
name of the IP, i.e.
f = show (?stk :: CallStack)
g = show (?loc :: CallStack)
......
......@@ -47,8 +47,8 @@ The highlights, since the 7.10 branch, are:
- :ghc-flag:`-XDuplicateRecordFields`, allowing multiple datatypes to declare the same
record field names provided they are used unambiguously (see :ref:`duplicate-record-fields`)
- Support for implicit parameters providing light-weight
:ref:`callstacks and source locations <implicit-callstacks>`
- Support for lightweight
:ref:`callstacks and source locations <hascallstack>`
- User-defined error messages for type errors
......@@ -85,24 +85,20 @@ Language
-- | Just a normal sum
Sum :: Int -> Int -> Expr Int
- Implicit parameters of the new ``base`` type :base-ref:`GHC.Stack.CallStack <GHC-Stack.html>`
are treated specially in function calls, the solver automatically
appends the source location of the call to the ``CallStack`` in
the environment. For example ::
- The new ``base`` constraint :base-ref:`GHC.Stack.HasCallStack <GHC-Stack.html>`
can be used by functions to request a partial call-stack. For example ::
myerror :: (?callStack :: CallStack) => String -> a
myerror msg = error (msg ++ "\n" ++ prettyCallStack ?callStack)
errorWithCallStack :: HasCallStack => String -> a
errorWithCallStack msg = error (msg ++ "\n" ++ prettyCallStack callStack)
ghci> myerror "die"
ghci> errorWithCallStack "die"
*** Exception: die
CallStack (from ImplicitParams):
myerror, called at <interactive>:2:1 in interactive:Ghci1
CallStack (from HasCallStack):
errorWithCallStack, called at <interactive>:2:1 in interactive:Ghci1
prints the call-site of ``myerror``. The name of the implicit
parameter does not matter, but within ``base`` we call it
``?callStack``.
prints the call-site of ``errorWithCallStack``.
See :ref:`lib-base` for a description of the ``CallStack`` type.
See :ref:`hascallstack` for a description of ``HasCallStack``.
- GHC now supports visible type application, allowing
programmers to easily specify how type parameters should be
......
......@@ -1136,8 +1136,8 @@ conjunction with ``-fexternal-interpreter``.
There are three ways to get access to the current call stack.
- ``error`` and ``undefined`` automatically attach the current stack
to the error message. This often complements the implicit stack
stack (see :ref:`implicit-callstacks`), so both call stacks are
to the error message. This often complements the ``HasCallStack``
stack (see :ref:`hascallstack`), so both call stacks are
shown.
- ``Debug.Trace.traceStack`` is a version of ``Debug.Trace.trace``
......
......@@ -8040,83 +8040,6 @@ a type signature for ``y``, then ``y`` will get type
``let`` will see the inner binding of ``?x``, so ``(f 9)`` will return
``14``.
.. _implicit-callstacks:
Implicit CallStacks
~~~~~~~~~~~~~~~~~~~
Implicit parameters of the new ``base`` type ``GHC.Stack.CallStack`` are
treated specially in function calls, the solver automatically pushes
the source location of the call onto the ``CallStack`` in the
environment. For example
::
myerror :: (?callStack :: CallStack) => String -> a
myerror msg = error (msg ++ "\n" ++ prettyCallStack ?callStack)
ghci> myerror "die"
*** Exception: die
CallStack (from ImplicitParams):
myerror, called at <interactive>:2:1 in interactive:Ghci1
prints the call-site of ``myerror``. The name of the implicit
parameter does not matter, but within ``base`` we call it
``?callStack``.
The ``CallStack`` will only extend as far as the types allow it, for
example
.. code-block:: none
head :: (?callStack :: CallStack) => [a] -> a
head [] = myerror "empty"
head (x:xs) = x
bad :: Int
bad = head []
ghci> bad
*** Exception: empty
CallStack (from ImplicitParams):
myerror, called at Bad.hs:8:15 in main:Bad
head, called at Bad.hs:12:7 in main:Bad
includes the call-site of ``myerror`` in ``head``, and of ``head`` in
``bad``, but not the call-site of ``bad`` at the GHCi prompt.
GHC will never report an unbound implicit ``CallStack``, and will
instead default such occurrences to the empty ``CallStack``.
``CallStack`` is kept abstract, but GHC provides a function ::
getCallStack :: CallStack -> [(String, SrcLoc)]
to access the individual call-sites in the stack. The ``String`` is the
name of the function that was called, and the ``SrcLoc`` provides the
package, module, and file name, as well as the line and column numbers.
GHC will infer ``CallStack`` constraints using the same rules as for
ordinary implicit parameters.
``GHC.Stack`` additionally exports a function ``freezeCallStack`` that
allows users to freeze a ``CallStack``, preventing any future push
operations from having an effect. This can be used by library authors
to prevent ``CallStack``\s from exposing unnecessary implementation
details. Consider the ``head`` example above, the ``myerror`` line in
the printed stack is not particularly enlightening, so we might choose
to suppress it by freezing the ``CallStack`` that we pass to ``myerror``.
.. code-block:: none
head :: (?callStack :: CallStack) => [a] -> a
head [] = let ?callStack = freezeCallStack ?callStack in myerror "empty"
head (x:xs) = x
ghci> head []]
*** Exception: empty
CallStack (from ImplicitParams):
head, called at Bad.hs:12:7 in main:Bad
.. _kinding:
......@@ -12879,3 +12802,112 @@ intuitive: ::
let f = /\a \(d::Eq a). fst (member, True) in body
Note that the bang has no effect at all in this case
.. _hascallstack:
HasCallStack
============
``GHC.Stack.HasCallStack`` is a lightweight method of obtaining a
partial call-stack at any point in the program.
A function can request its call-site with the ``HasCallStack`` constraint.
For example, we can define ::
errorWithCallStack :: HasCallStack => String -> a
as a variant of ``error`` that will get its call-site. We can access the
call-stack inside ``errorWithCallStack`` with ``GHC.Stack.callStack``. ::
errorWithCallStack :: HasCallStack => String -> a
errorWithCallStack msg = error (msg ++ "\n" ++ prettyCallStack callStack)
Thus, if we call ``errorWithCallStack`` we will get a formatted call-stack
alongside our error message.
.. code-block:: none
ghci> errorWithCallStack "die"
*** Exception: die
CallStack (from HasCallStack):
errorWithCallStack, called at <interactive>:2:1 in interactive:Ghci1
The ``CallStack`` will only extend as far as the types allow it, for
example ::
head :: HasCallStack => [a] -> a
head [] = errorWithCallStack "empty"
head (x:xs) = x
bad :: Int
bad = head []
.. code-block:: none
ghci> bad
*** Exception: empty
CallStack (from HasCallStack):
errorWithCallStack, called at Bad.hs:8:15 in main:Bad
head, called at Bad.hs:12:7 in main:Bad
includes the call-site of ``errorWithCallStack`` in ``head``,
and of ``head`` in ``bad``,
but not the call-site of ``bad`` at the GHCi prompt.
GHC solves ``HasCallStack`` constraints in three steps:
1. If there is a ``CallStack`` in scope -- i.e. the enclosing function
has a ``HasCallStack`` constraint -- GHC will push the new call-site
onto the existing ``CallStack``.
2. If there is no ``CallStack`` in scope -- e.g. in the GHCi session
above -- and the enclosing definition does not have an explicit
type signature, GHC will infer a ``HasCallStack`` constraint for the
enclosing definition (subject to the monomorphism restriction).
3. If there is no ``CallStack`` in scope and the enclosing definition
has an explicit type signature, GHC will solve the ``HasCallStack``
constraint for the singleton ``CallStack`` containing just the
current call-site.
``CallStack`` is kept abstract, but GHC provides a function ::
getCallStack :: CallStack -> [(String, SrcLoc)]
to access the individual call-sites in the stack. The ``String`` is the
name of the function that was called, and the ``SrcLoc`` provides the
package, module, and file name, as well as the line and column numbers.
``GHC.Stack`` additionally exports a function ``withFrozenCallStack`` that
allows users to freeze the current ``CallStack``, preventing any future push
operations from having an effect. This can be used by library authors
to prevent ``CallStack``\s from exposing unnecessary implementation
details. Consider the ``head`` example above, the ``errorWithCallStack`` line in
the printed stack is not particularly enlightening, so we might choose
to suppress it by freezing the ``CallStack`` that we pass to ``errorWithCallStack``. ::
head :: HasCallStack => [a] -> a
head [] = withFrozenCallStack (errorWithCallStack "empty")
head (x:xs) = x
.. code-block:: none
ghci> head []
*** Exception: empty
CallStack (from HasCallStack):
head, called at Bad.hs:12:7 in main:Bad
**NOTE**: The intrepid user may notice that ``HasCallStack`` is just an
alias for an implicit parameter ``?callStack :: CallStack``. This is an
implementation detail and **should not** be considered part of the
``CallStack`` API, we may decide to change the implementation in the
future.
Compared with other sources of stack traces
-------------------------------------------
``HasCallStack`` does not interact with the RTS and does not require
compilation with ``-prof``. On the other hand, as the ``CallStack`` is
built up explicitly via the ``HasCallStack`` constraints, it will
generally not contain as much information as the simulated call-stacks
maintained by the RTS.
......@@ -34,25 +34,30 @@ import {-# SOURCE #-} GHC.Exception( errorCallWithCallStackException )
-- | 'error' stops execution and displays an error message.
error :: forall (v :: Levity). forall (a :: TYPE v).
(?callStack :: CallStack) => [Char] -> a
HasCallStack => [Char] -> a
error s = raise# (errorCallWithCallStackException s ?callStack)
-- Bleh, we should be using 'GHC.Stack.callStack' instead of
-- '?callStack' here, but 'GHC.Stack.callStack' depends on
-- 'GHC.Stack.popCallStack', which is partial and depends on
-- 'error'.. Do as I say, not as I do.
-- | A variant of 'error' that does not produce a stack trace.
--
-- @since 4.9.0.0
errorWithoutStackTrace :: forall (v :: Levity). forall (a :: TYPE v).
[Char] -> a
errorWithoutStackTrace s
= let ?callStack = freezeCallStack ?callStack
in error s
{-# NOINLINE errorWithoutStackTrace #-}
errorWithoutStackTrace s =
-- we don't have withFrozenCallStack yet, so we just inline the definition
let ?callStack = freezeCallStack emptyCallStack
in error s
-- Note [Errors in base]
-- ~~~~~~~~~~~~~~~~~~~~~
-- As of base-4.9.0.0, `error` produces a stack trace alongside the
-- error message using the Implicit CallStack machinery. This provides
-- error message using the HasCallStack machinery. This provides
-- a partial stack trace, containing the call-site of each function
-- with a (?callStack :: CallStack) implicit parameter constraint.
-- with a HasCallStack constraint.
--
-- In base, however, the only functions that have such constraints are
-- error and undefined, so the stack traces from partial functions in
......@@ -70,7 +75,7 @@ errorWithoutStackTrace s
-- messages which are more appropriate to the context in which 'undefined'
-- appears.
undefined :: forall (v :: Levity). forall (a :: TYPE v).
(?callStack :: CallStack) => a
HasCallStack => a
undefined = error "Prelude.undefined"
-- | Used for compiler-generated error message;
......
......@@ -200,7 +200,7 @@ showCCSStack stk = "CallStack (from -prof):" : map (" " ++) (reverse stk)
-- prettySrcLoc and prettyCallStack are defined here to avoid hs-boot
-- files. See Note [Definition of CallStack]
-- | Pretty print 'SrcLoc'
-- | Pretty print a 'SrcLoc'.
--
-- @since 4.8.1.0
prettySrcLoc :: SrcLoc -> String
......@@ -212,7 +212,7 @@ prettySrcLoc SrcLoc {..}
, srcLocPackage, ":", srcLocModule
]
-- | Pretty print 'CallStack'
-- | Pretty print a 'CallStack'.
--
-- @since 4.8.1.0
prettyCallStack :: CallStack -> String
......@@ -221,7 +221,7 @@ prettyCallStack = intercalate "\n" . prettyCallStackLines
prettyCallStackLines :: CallStack -> [String]
prettyCallStackLines cs = case getCallStack cs of
[] -> []
stk -> "CallStack (from ImplicitParams):"
stk -> "CallStack (from HasCallStack):"
: map ((" " ++) . prettyCallSite) stk
where
prettyCallSite (f, loc) = f ++ ", called at " ++ prettySrcLoc loc
......
......@@ -17,14 +17,16 @@
{-# LANGUAGE MagicHash, NoImplicitPrelude, ImplicitParams, RankNTypes #-}
module GHC.Stack (
-- * Call stacks
errorWithStackTrace,
-- * Profiling call stacks
currentCallStack,
whoCreated,
errorWithStackTrace,
-- * Implicit parameter call stacks
CallStack, emptyCallStack, freezeCallStack, getCallStack, popCallStack,
prettyCallStack, pushCallStack, withFrozenCallStack,
-- * HasCallStack call stacks
CallStack, HasCallStack, callStack, emptyCallStack, freezeCallStack,
getCallStack, popCallStack, prettyCallStack, pushCallStack,
withFrozenCallStack,
-- * Source locations
SrcLoc(..), prettySrcLoc,
......@@ -75,16 +77,25 @@ popCallStack stk = case stk of
EmptyCallStack -> errorWithoutStackTrace "popCallStack: empty stack"
PushCallStack _ stk' -> stk'
FreezeCallStack _ -> stk
{-# INLINE popCallStack #-}
-- | Return the current 'CallStack'.
--
-- Does *not* include the call-site of 'callStack'.
--
-- @since 4.9.0.0
callStack :: HasCallStack => CallStack
callStack = popCallStack ?callStack
{-# INLINE callStack #-}
-- | Perform some computation without adding new entries to the 'CallStack'.
--
-- @since 4.9.0.0
withFrozenCallStack :: (?callStack :: CallStack)
=> ( (?callStack :: CallStack) => a )
withFrozenCallStack :: HasCallStack
=> ( HasCallStack => a )
-> a
withFrozenCallStack do_this =
-- we pop the stack before freezing it to remove
-- withFrozenCallStack's call-site
let ?callStack = freezeCallStack (popCallStack ?callStack)
-- we pop the stack before freezing it to remove
-- withFrozenCallStack's call-site
let ?callStack = freezeCallStack (popCallStack callStack)
in do_this
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# OPTIONS_HADDOCK hide #-}
-- we hide this module from haddock to enforce GHC.Stack as the main
-- access point.
......@@ -13,15 +19,17 @@
-- Stability : internal
-- Portability : non-portable (GHC Extensions)
--
-- type definitions for call-stacks via implicit parameters.
-- type definitions for implicit call-stacks.
-- Use "GHC.Stack" from the base package instead of importing this
-- module directly.
--
-----------------------------------------------------------------------------
module GHC.Stack.Types (
-- * Implicit parameter call stacks
CallStack(..), emptyCallStack, freezeCallStack, getCallStack, pushCallStack,
-- * Implicit call stacks
CallStack(..), HasCallStack,
emptyCallStack, freezeCallStack, getCallStack, pushCallStack,
-- * Source locations
SrcLoc(..)
) where
......@@ -49,43 +57,75 @@ import GHC.Integer ()
-- Explicit call-stacks built via ImplicitParams
----------------------------------------------------------------------
-- | Implicit @CallStack@s are an alternate method of obtaining the call stack
-- at a given point in the program.
-- | Request a CallStack.
--
-- GHC has two built-in rules for solving implicit-parameters of type
-- @CallStack@.
-- NOTE: The implicit parameter @?callStack :: CallStack@ is an
-- implementation detail and __should not__ be considered part of the
-- 'CallStack' API, we may decide to change the implementation in the
-- future.
--
-- 1. If the @CallStack@ occurs in a function call, it appends the
-- source location of the call to the @CallStack@ in the environment.
-- 2. @CallStack@s that cannot be solved normally (i.e. unbound
-- occurrences) are defaulted to the empty @CallStack@.
-- @since 4.9.0.0
type HasCallStack = (?callStack :: CallStack)
-- | 'CallStack's are a lightweight method of obtaining a
-- partial call-stack at any point in the program.
--
-- Otherwise implicit @CallStack@s behave just like ordinary implicit
-- parameters. For example:
-- A function can request its call-site with the 'HasCallStack' constraint.
-- For example, we can define
--
-- @
-- myerror :: (?callStack :: CallStack) => String -> a
-- myerror msg = error (msg ++ "\n" ++ prettyCallStack ?callStack)
-- errorWithCallStack :: HasCallStack => String -> a
-- @
--
-- Will produce the following when evaluated,
-- as a variant of @error@ that will get its call-site. We can access the
-- call-stack inside @errorWithCallStack@ with 'GHC.Stack.callStack'.
--
-- @
-- ghci> myerror "die"
-- *** Exception: die
-- CallStack (from ImplicitParams):
-- myerror, called at <interactive>:2:1 in interactive:Ghci1
-- errorWithCallStack :: HasCallStack => String -> a
-- errorWithCallStack msg = error (msg ++ "\n" ++ prettyCallStack callStack)
-- @
--
-- @CallStack@s do not interact with the RTS and do not require compilation with
-- @-prof@. On the other hand, as they are built up explicitly using
-- implicit-parameters, they will generally not contain as much information as
-- the simulated call-stacks maintained by the RTS.
-- Thus, if we call @errorWithCallStack@ we will get a formatted call-stack
-- alongside our error message.
--
--
-- >>> errorWithCallStack "die"
-- *** Exception: die
-- CallStack (from HasCallStack):
-- errorWithCallStack, called at <interactive>:2:1 in interactive:Ghci1
--
--
-- GHC solves 'HasCallStack' constraints in three steps:
--
-- 1. If there is a 'CallStack' in scope -- i.e. the enclosing function
-- has a 'HasCallStack' constraint -- GHC will append the new
-- call-site to the existing 'CallStack'.
--
-- 2. If there is no 'CallStack' in scope -- e.g. in the GHCi session
-- above -- and the enclosing definition does not have an explicit
-- type signature, GHC will infer a 'HasCallStack' constraint for the
-- enclosing definition (subject to the monomorphism restriction).
--
-- A @CallStack@ is a @[(String, SrcLoc)]@. The @String@ is the name of
-- 3. If there is no 'CallStack' in scope and the enclosing definition
-- has an explicit type signature, GHC will solve the 'HasCallStack'
-- constraint for the singleton 'CallStack' containing just the
-- current call-site.
--
-- 'CallStack's do not interact with the RTS and do not require compilation
-- with @-prof@. On the other hand, as they are built up explicitly via the
-- 'HasCallStack' constraints, they will generally not contain as much
-- information as the simulated call-stacks maintained by the RTS.
--
-- A 'CallStack' is a @[(String, SrcLoc)]@. The @String@ is the name of
-- function that was called, the 'SrcLoc' is the call-site. The list is
-- ordered with the most recently called function at the head.
--
-- NOTE: The intrepid user may notice that 'HasCallStack' is just an
-- alias for an implicit parameter @?callStack :: CallStack@. This is an
-- implementation detail and __should not__ be considered part of the
-- 'CallStack' API, we may decide to change the implementation in the
-- future.
--
-- @since 4.8.1.0
data CallStack
= EmptyCallStack
......@@ -110,7 +150,7 @@ getCallStack stk = case stk of
-- Note [Definition of CallStack]
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Implicit CallStacks are defined very early in base because they are
-- CallStack is defined very early in base because it is
-- used by error and undefined. At this point in the dependency graph,
-- we do not have enough functionality to (conveniently) write a nice
-- pretty-printer for CallStack. The sensible place to define the
......@@ -143,6 +183,7 @@ emptyCallStack :: CallStack
emptyCallStack = EmptyCallStack
{-# INLINE emptyCallStack #-}
-- | Freeze a call-stack, preventing any further call-sites from being appended.
--
-- prop> pushCallStack callSite (freezeCallStack callStack) = freezeCallStack callStack
......
......@@ -1689,13 +1689,17 @@ def normalise_whitespace( str ):
# Merge contiguous whitespace characters into a single space.
return ' '.join(w for w in str.split())
callSite_re = re.compile(r', called at (.+):[\d]+:[\d]+ in [\w\-\.]+:')
def normalise_callstacks(str):
def repl(matches):
location = matches.group(1)
location = normalise_slashes_(location)
return ', called at {0}:<line>:<column> in <package-id>:'.format(location)
# Ignore line number differences in call stacks (#10834).
return re.sub(', called at (.+):[\\d]+:[\\d]+ in [\\w\-\.]+:', repl, str)
str1 = re.sub(callSite_re, repl, str)
# Ignore the change in how we identify implicit call-stacks
return str1.replace('from ImplicitParams', 'from HasCallStack')
tyCon_re = re.compile(r'TyCon\s*\d+L?\#\#\s*\d+L?\#\#\s*', flags=re.MULTILINE)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment