CoreLint and StgCmm disagree on safety of unsafeCoercions
CoreLint has this Note on bad unsafe coercions:
Note [Bad unsafe coercion]
~~~~~~~~~~~~~~~~~~~~~~~~~~
For discussion see https://gitlab.haskell.org/ghc/ghc/wikis/bad-unsafe-coercions
Linter introduces additional rules that checks improper coercion between
different types, called bad coercions. Following coercions are forbidden:
(a) coercions between boxed and unboxed values;
(b) coercions between unlifted values of the different sizes, here
active size is checked, i.e. size of the actual value but not
the space allocated for value;
(c) coercions between floating and integral boxed values, this check
is not yet supported for unboxed tuples, as no semantics were
specified for that;
(d) coercions from / to vector type
(e) If types are unboxed tuples then tuple (# A_1,..,A_n #) can be
coerced to (# B_1,..,B_m #) if n=m and for each pair A_i, B_i rules
(a-e) holds.
(b) is implemented as this check
checkWarnL (isUnBoxed rep1 == isUnBoxed rep2)
(report "between unboxed and boxed value")
checkWarnL (TyCon.primRepSizeB dflags rep1 == TyCon.primRepSizeB dflags rep2)
(report "between unboxed values of different size")
where
isUnBoxed :: PrimRep -> Bool
isUnBoxed = not . isGcPtrRep
This check accepts coercions from Int#
to Int64#
on a 64-bit system as those
have the same size on 64-bit. However StgCmmExpr.cgCase
has a more strict
version of this check, when compiling a code like
case (foo :: Int64#) of (bndr :: Int#) { ... }
The check:
cgCase (StgApp v []) bndr alt_type@(PrimAlt _) alts
| isUnliftedType (idType v) -- Note [Dodgy unsafeCoerce 1]
|| reps_compatible
= -- assignment suffices for unlifted types
do { dflags <- getDynFlags
; unless reps_compatible $
pprPanic "cgCase: reps do not match, perhaps a dodgy unsafeCoerce?"
(pp_bndr v $$ pp_bndr bndr)
; ...
}
where
reps_compatible = ((==) `on` (primRepSlot . idPrimRep)) v bndr
The problem is idPrimRep
s of an Int#
and an Int64#
are different even on a
64-bit system: one gets IntRep
and the other one gets Int64Rep
. Then
primRepSlot
maps these to two different slots. (reminder: primRepSlot
was introduced for unboxed sums -- two types with same slots can use the same
memory location in an unboxed sum type)
What to do? Unfortunately idPrimRep
is not documented so it's not clear what
the intent is, but it's used during Cmm generation. Given that Int#
and
Int64#
are literally the same thing on a 64-bit system, I think it makes sense
to return the same PrimRep
for these types. A simple fix would be to just
update idPrimRep
to take a DynFlags
argument and return the same PrimRep
.
I'm not sure what's the refactoring for this change though -- idPrimRep
calls
runtimeRepPrimRep
, which calls the RuntimeRep
function returned by
tyConRuntimeRepInfo
, but int64PrimTyCon
and intPrimTyCon
are PrimTyCon
s,
so they don't have a promDcRepInfo
... I don't understand how this works yet.
I suspect something in TysWiredIn
will have to change.
(CC @simonpj)