summaryrefslogtreecommitdiff
path: root/libraries/base/Unsafe/Coerce.hs
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/base/Unsafe/Coerce.hs')
-rw-r--r--libraries/base/Unsafe/Coerce.hs346
1 files changed, 294 insertions, 52 deletions
diff --git a/libraries/base/Unsafe/Coerce.hs b/libraries/base/Unsafe/Coerce.hs
index 86e2d9fd65..bad2e5bea6 100644
--- a/libraries/base/Unsafe/Coerce.hs
+++ b/libraries/base/Unsafe/Coerce.hs
@@ -1,62 +1,304 @@
-{-# LANGUAGE Unsafe #-}
-{-# LANGUAGE NoImplicitPrelude, MagicHash #-}
+-- We don't to strictness analysis on this file to avoid turning loopy unsafe
+-- equality terms below to actual loops. Details in (U5) of
+-- Note [Implementing unsafeCoerce]
+{-# OPTIONS_GHC -fno-strictness #-}
------------------------------------------------------------------------------
--- |
--- Module : Unsafe.Coerce
--- Copyright : Malcolm Wallace 2006
--- License : BSD-style (see the LICENSE file in the distribution)
+{-# LANGUAGE Unsafe, NoImplicitPrelude, MagicHash, GADTs, TypeApplications,
+ ScopedTypeVariables, TypeOperators, KindSignatures, PolyKinds,
+ StandaloneKindSignatures, DataKinds #-}
+
+module Unsafe.Coerce
+ ( unsafeCoerce, unsafeCoerceUnlifted, unsafeCoerceAddr
+ , unsafeEqualityProof
+ , UnsafeEquality (..)
+ , unsafeCoerce#
+ ) where
+
+import GHC.Arr (amap) -- For amap/unsafeCoerce rule
+import GHC.Base
+import GHC.Integer () -- See Note [Depend on GHC.Integer] in GHC.Base
+import GHC.Natural () -- See Note [Depend on GHC.Natural] in GHC.Base
+
+import GHC.Types
+
+{- Note [Implementing unsafeCoerce]
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The implementation of unsafeCoerce is surprisingly subtle.
+This Note describes the moving parts. You will find more
+background in MR !1869 and ticket #16893.
+
+The key challenge is this. Suppose we have
+ case sameTypeRep t1 t2 of
+ False -> blah2
+ True -> ...(case (x |> UnsafeCo @t1 @t2) of { K -> blah })...
+
+The programmer thinks that the unsafeCoerce from 't1' to 't2' is safe,
+because it is justified by a runtime test (sameTypeRep t1 t2).
+It used to compile to a cast, with a magical 'UnsafeCo' coercion.
+
+But alas, nothing then stops GHC floating that call to unsafeCoerce
+outwards so we get
+ case (x |> UnsafeCo @t1 @t2) of
+ K -> case sameTypeRep t1 t2 of
+ False -> blah2
+ True -> ...blah...
+
+and this is utterly wrong, because the unsafeCoerce is being performed
+before the dynamic test. This is exactly the setup in #16893.
+
+The solution is this:
+
+* In the library Unsafe.Coerce we define:
+
+ unsafeEqualityProof :: forall k (a :: k) (b :: k).
+ UnsafeEquality a b
+
+* It uses a GADT, Unsafe.Coerce.UnsafeEquality, that is exactly like :~:
+
+ data UnsafeEquality (a :: k) (b :: k) where
+ UnsafeRefl :: UnsafeEquality a a
+
+* We can now define Unsafe.Coerce.unsafeCoerce very simply:
+
+ unsafeCoerce :: forall (a :: Type) (b :: Type) . a -> b
+ unsafeCoerce x = case unsafeEqualityProof @a @b of
+ UnsafeRefl -> x
+
+ There is nothing special about unsafeCoerce; it is an
+ ordinary library definition, and can be freely inlined.
+
+Now our bad case can't happen. We'll have
+ case unsafeEqualityProof @t1 @t2 of
+ UnsafeRefl (co :: t1 ~ t2) -> ....(x |> co)....
+
+and the (x |> co) mentions the evidence 'co', which prevents it
+floating.
+
+But what stops the whole (case unsafeEqualityProof of ...) from
+floating? Answer: we never float a case on a redex that can fail
+outside a conditional. See Primop.hs,
+Note [Transformations affected by can_fail and has_side_effects].
+And unsafeEqualityProof (being opaque) is definitely treated as
+can-fail.
+
+While unsafeCoerce is a perfectly ordinary function that needs no
+special treatment, Unsafe.Coerce.unsafeEqualityProof is magical, in
+several ways
+
+(U1) unsafeEqualityProof is /never/ inlined.
+
+(U2) In CoreToStg.coreToStg, we transform
+ case unsafeEqualityProof of UnsafeRefl -> blah
+ ==>
+ blah
+
+ This eliminates the overhead of evaluating the unsafe
+ equality proof.
+
+ Any /other/ occurrence of unsafeEqualityProof is left alone.
+ For example you could write
+ f :: UnsafeEquality a b -> blah
+ f eq_proof = case eq_proof of UnsafeRefl -> ...
+ (Nothing special about that.) In a call, you might write
+ f unsafeEqualityProof
+
+ and we'll generate code simply by passing the top-level
+ unsafeEqualityProof to f. As (U5) says, it is implemented as
+ UnsafeRefl so all is good.
+
+(U3) In GHC.CoreToStg.Prep.cpeRhsE, if we see
+ let x = case unsafeEqualityProof ... of
+ UnsafeRefl -> K e
+ in ...
+
+ there is a danger that we'll go to
+ let x = case unsafeEqualityProof ... of
+ UnsafeRefl -> let a = e in K a
+ in ...
+
+ and produce a thunk even after discarding the unsafeEqualityProof.
+ So instead we float out the case to give
+ case unsafeEqualityProof ... of { UnsafeRefl ->
+ let a = K e
+ x = K a
+ in ...
+ Flaoting the case is OK here, even though it broardens the
+ scope, becuase we are done with simplification.
+
+(U4) GHC.CoreToStg.Prep.cpeExprIsTrivial anticipated the
+ upcoming discard of unsafeEqualityProof.
+
+(U5) The definition of unsafeEqualityProof in Unsafe.Coerce
+ looks very strange:
+ unsafeEqualityProof = case unsafeEqualityProof @a @b of
+ UnsafeRefl -> UnsafeRefl
+
+ It looks recursive! But the above-mentioned CoreToStg
+ transform will change it to
+ unsafeEqualityProof = UnsafeRefl
+ And that is exactly the code we want! For example, if we say
+ f unsafeEqualityProof
+ we want to pass an UnsafeRefl constructor to f.
+
+ We turn off strictness analysis in this module, otherwise
+ the strictness analyser will mark unsafeEqualityProof as
+ bottom, which is utterly wrong.
+
+(U6) The UnsafeEquality data type is also special in one way.
+ Consider this piece of Core
+ case unsafeEqualityProof @Int @Bool of
+ UnsafeRefl (g :: Int ~# Bool) -> ...g...
+
+ The simplifier normally eliminates case alternatives with
+ contradicatory GADT data constructors; here we bring into
+ scope evidence (g :: Int~Bool). But we do not want to
+ eliminate this particular alternative! So we put a special
+ case into DataCon.dataConCannotMatch to account for this.
+
+(U7) We add a built-in RULE
+ unsafeEqualityProof k t t ==> UnsafeRefl (Refl t)
+ to simplify the ase when the two tpyes are equal.
+
+(U8) The is a super-magic RULE in GHC.base
+ map cocerce = coerce
+ (see Note [Getting the map/coerce RULE to work] in CoreOpt)
+ But it's all about turning coerce into a cast, and unsafeCoerce
+ no longer does that. So we need a separate map/unsafeCoerce
+ RULE, in this module.
+
+There are yet more wrinkles
+
+(U9) unsafeCoerce works only over types of kind `Type`.
+ But what about other types? In Unsafe.Coerce we also define
+
+ unsafeCoerceUnlifted :: forall (a :: TYPE UnliftedRep)
+ (b :: TYPE UnliftedRep).
+ a -> b
+ unsafeCoerceUnlifted x
+ = case unsafeEqualityProof @a @b of
+ UnsafeRefl -> x
+
+ and similarly for unsafeCoerceAddr, unsafeCoerceInt, etc.
+
+(U10) We also want a levity-polymorphic unsafeCoerce#:
+
+ unsafeCoerce# :: forall (r1 :: RuntimeRep) (r2 :: RuntimeRep)
+ (a :: TYPE r1) (b :: TYPE r2).
+ a -> b
+
+ This is even more dangerous, because it converts between two types
+ *with different runtime representations*!! Our goal is to deprecate
+ it entirely. But for now we want it.
+
+ But having it is hard! It is defined by a kind of stub in Unsafe.Coerce,
+ and overwritten by the desugarer. See Note [Wiring in unsafeCoerce#]
+ in Desugar. Here's the code for it
+ unsafeCoerce# x = case unsafeEqualityProof @r1 @r2 of UnsafeRefl ->
+ case unsafeEqualityProof @a @b of UnsafeRefl ->
+ x
+ Notice that we can define this kind-/heterogeneous/ function by calling
+ the kind-/homogeneous/ unsafeEqualityProof twice.
+
+ See Note [Wiring in unsafeCoerce#] in Desugar.
+
+(U11) We must also be careful to discard unsafeEqualityProof in the
+ bytecode generator; see ByteCodeGen.bcView. Here we don't really
+ care about fast execution, but (annoyingly) we /do/ care about the
+ GHCi debugger, and GHCi itself uses unsafeCoerce.
+
+ Moreover, in TcRnDriver.tcGhciStmts we use unsafeCoerce#, rather
+ than the more kosher unsafeCoerce, becuase (with -O0) the latter
+ may not be inlined.
+
+ Sigh
+-}
+
+-- | This type is treated magically within GHC. Any pattern match of the
+-- form @case unsafeEqualityProof of UnsafeRefl -> body@ gets transformed just into @body@.
+-- This is ill-typed, but the transformation takes place after type-checking is
+-- complete. It is used to implement 'unsafeCoerce'. You probably don't want to
+-- use 'UnsafeRefl' in an expression, but you might conceivably want to pattern-match
+-- on it. Use 'unsafeEqualityProof' to create one of these.
+data UnsafeEquality a b where
+ UnsafeRefl :: UnsafeEquality a a
+
+{-# NOINLINE unsafeEqualityProof #-}
+unsafeEqualityProof :: forall a b . UnsafeEquality a b
+-- See (U5) of Note [Implementing unsafeCoerce]
+unsafeEqualityProof = case unsafeEqualityProof @a @b of UnsafeRefl -> UnsafeRefl
+
+{-# INLINE [1] unsafeCoerce #-}
+-- The INLINE will almost certainly happen automatically, but it's almost
+-- certain to generate (slightly) better code, so let's do it. For example
+--
+-- case (unsafeCoerce blah) of ...
+--
+-- will turn into
+--
+-- case unsafeEqualityProof of UnsafeRefl -> case blah of ...
+--
+-- which is definitely better.
+
+-- | Coerce a value from one type to another, bypassing the type-checker.
+--
+-- There are several legitimate ways to use 'unsafeCoerce':
--
--- Maintainer : libraries@haskell.org
--- Stability : experimental
--- Portability : portable
+-- 1. To coerce e.g. @Int@ to @HValue@, put it in a list of @HValue@,
+-- and then later coerce it back to @Int@ before using it.
--
--- The highly unsafe primitive 'unsafeCoerce' converts a value from any
--- type to any other type. Needless to say, if you use this function,
--- it is your responsibility to ensure that the old and new types have
--- identical internal representations, in order to prevent runtime corruption.
+-- 2. To produce e.g. @(a+b) :~: (b+a)@ from @unsafeCoerce Refl@.
+-- Here the two sides really are the same type -- so nothing unsafe is happening
+-- -- but GHC is not clever enough to see it.
--
--- The types for which 'unsafeCoerce' is representation-safe may differ
--- from compiler to compiler (and version to version).
+-- 3. In @Data.Typeable@ we have
--
--- * Documentation for correct usage in GHC will be found under
--- 'unsafeCoerce#' in "GHC.Base" (around which 'unsafeCoerce' is just a
--- trivial wrapper).
+-- @
+-- eqTypeRep :: forall k1 k2 (a :: k1) (b :: k2).
+-- TypeRep a -> TypeRep b -> Maybe (a :~~: b)
+-- eqTypeRep a b
+-- | sameTypeRep a b = Just (unsafeCoerce HRefl)
+-- | otherwise = Nothing
+-- @
--
--- * In nhc98, the only representation-safe coercions are between
--- 'Prelude.Enum' types with the same range (e.g. 'Prelude.Int',
--- 'Data.Int.Int32', 'Prelude.Char', 'Data.Word.Word32'), or between a
--- newtype and the type that it wraps.
+-- Here again, the @unsafeCoerce HRefl@ is safe, because the two types really
+-- are the same -- but the proof of that relies on the complex, trusted
+-- implementation of @Typeable@.
--
------------------------------------------------------------------------------
+-- 4. The "reflection trick", which takes advantanage of the fact that in
+-- @class C a where { op :: ty }@, we can safely coerce between @C a@ and @ty@
+-- (which have different kinds!) because it's really just a newtype.
+-- Note: there is /no guarantee, at all/ that this behavior will be supported
+-- into perpetuity.
+unsafeCoerce :: forall (a :: Type) (b :: Type) . a -> b
+unsafeCoerce x = case unsafeEqualityProof @a @b of UnsafeRefl -> x
-module Unsafe.Coerce (unsafeCoerce) where
+unsafeCoerceUnlifted :: forall (a :: TYPE 'UnliftedRep) (b :: TYPE 'UnliftedRep) . a -> b
+-- Kind-homogeneous, but levity monomorphic (TYPE UnliftedRep)
+unsafeCoerceUnlifted x = case unsafeEqualityProof @a @b of UnsafeRefl -> x
-import GHC.Integer () -- See Note [Depend on GHC.Integer] in GHC.Base
-import GHC.Natural () -- See Note [Depend on GHC.Natural] in GHC.Base
-import GHC.Prim (unsafeCoerce#)
-
-local_id :: a -> a
-local_id x = x -- See Note [Mega-hack for coerce]
-
-{- Note [Mega-hack for coerce]
-
-If we just say
- unsafeCoerce x = unsafeCoerce# x
-then the simple-optimiser that the desugarer runs will eta-reduce to
- unsafeCoerce :: forall (a:*) (b:*). a -> b
- unsafeCoerce = unsafeCoerce#
-But we shouldn't be calling unsafeCoerce# in a higher
-order way; it has a compulsory unfolding
- unsafeCoerce# a b x = x |> UnsafeCo a b
-and we really rely on it being inlined pronto. But the simple-optimiser doesn't.
-The identity function local_id delays the eta reduction just long enough
-for unsafeCoerce# to get inlined.
-
-Sigh. This is horrible, but then so is unsafeCoerce.
--}
+unsafeCoerceAddr :: forall (a :: TYPE 'AddrRep) (b :: TYPE 'AddrRep) . a -> b
+-- Kind-homogeneous, but levity monomorphic (TYPE AddrRep)
+unsafeCoerceAddr x = case unsafeEqualityProof @a @b of UnsafeRefl -> x
+
+-- | Highly, terribly dangerous coercion from one representation type
+-- to another. Misuse of this function can invite the garbage collector
+-- to trounce upon your data and then laugh in your face. You don't want
+-- this function. Really.
+unsafeCoerce# :: forall (r1 :: RuntimeRep) (r2 :: RuntimeRep)
+ (a :: TYPE r1) (b :: TYPE r2).
+ a -> b
+unsafeCoerce# = error "GHC internal error: unsafeCoerce# not unfolded"
+-- See (U10) of Note [Implementing unsafeCorece]
+-- The RHS is updated by Desugar.patchMagicDefns
+-- See Desugar Note [Wiring in unsafeCoerce#]
+
+{-# RULES
+-- See (U8) in Note [Implementing unsafeCoerce]
+
+-- unsafeCoerce version of the map/coerce rule defined in GHC.Base
+"map/unsafeCoerce" map unsafeCoerce = unsafeCoerce
-unsafeCoerce :: a -> b
-unsafeCoerce x = local_id (unsafeCoerce# x)
- -- See Note [Unsafe coerce magic] in basicTypes/MkId
- -- NB: Do not eta-reduce this definition (see above)
+-- unsafeCoerce version of the amap/coerce rule defined in GHC.Arr
+"amap/unsafeCoerce" amap unsafeCoerce = unsafeCoerce
+#-}