Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix https://github.com/haskell/vector/issues/223 #224

Merged
merged 1 commit into from
Oct 27, 2018

Conversation

idontgetoutmuch
Copy link
Member

No description provided.

@RyanGlScott
Copy link
Member

This is a good idea. From a quick search in GHCi, it looks like the following Vector data types have roles that are too permissive:

λ> :info Primitive.Vector
type role Primitive.Vector phantom -- should be `representational`
data Primitive.Vector a

λ> :info Primitive.MVector
type role Primitive.MVector nominal phantom -- should be `nominal representational`
data Primitive.MVector s a

λ> :info Storable.Vector 
type role Storable.Vector phantom -- should be `representational`
data Storable.Vector a

λ> :info Storable.MVector 
type role Storable.MVector phantom phantom -- should be `nominal representational`
data Storable.MVector s a

Can you fix these as well?

I'll leave some more specific comments inline.

@idontgetoutmuch
Copy link
Member Author

No problem - I'll do it now

Data/Vector.hs Outdated
@@ -5,6 +5,7 @@
, TypeFamilies
, Rank2Types
, BangPatterns
, RoleAnnotations
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This extension is only available on GHC 7.8 and later, so you'll need to use CPP to ensure that older GHCs can continue to compile this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is nothing in life simple?

Data/Vector.hs Outdated
@@ -204,6 +205,9 @@ import qualified Control.Applicative as Applicative
import qualified Data.Foldable as Foldable
import qualified Data.Traversable as Traversable

import Data.Coerce
import Foreign.C.Types
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to import these? This seems like an unforced change.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that was to test my change actually gave a type error - they should be removed

Data/Vector.hs Outdated
@@ -212,6 +216,7 @@ import Data.Monoid ( Monoid(..) )
import qualified GHC.Exts as Exts (IsList(..))
#endif

type role Vector representational
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, wrap this in CPP.

vector.cabal Outdated
@@ -179,6 +179,8 @@ source-repository head

test-suite vector-tests-O0
Default-Language: Haskell2010
if os(OSX)
extra-libraries: iconv
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what this has to do with the original issue. Perhaps this would be better suited to a separate patch.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure - the tests wouldn't build for me without this

@idontgetoutmuch
Copy link
Member Author

Hopefully all is now well

@RyanGlScott
Copy link
Member

LGTM. Thanks!

@RyanGlScott RyanGlScott merged commit e80833e into haskell:master Oct 27, 2018
@ekmett
Copy link
Member

ekmett commented Oct 28, 2018

Isn't representational too weak for safety? In general when you have a container that behaves based on the instances associated with a type (e.g. Map w/ Ord, etc) we want a nominal role for the argument. As it stands a user could still coerce one of them that was made representational. Having the same representation does not mean the Storable instances are compatible. They could well have different alignments or formats.

@RyanGlScott
Copy link
Member

Yes, that is a good observation. I'm somewhat reluctant to just slam on the brakes and make the arguments to Primitive.Vector and Storable.Vector nominal since it's possible that there is code in the wild that relies on coerce-ing underneath them (I would wager that @idontgetoutmuch probably has some examples of such code, in fact). Still, the safety argument is quite concerning...

Do you have an example of some code that would go wrong at runtime due to these roles being representational? That would make for a pretty compelling counterexample.

@Shimuuar
Copy link
Contributor

Totally contrived example for Storable. Data type with phantom type which mirrors C structure and phantom type encode layout (packed, standard C alignment, etc.).

@cartazio
Copy link
Contributor

cartazio commented Oct 29, 2018 via email

@RyanGlScott
Copy link
Member

My apologies for pushing it through quickly—I legitimately thought this was a no-brainer at the time. And in any case, this is a move in the right direction, since the roles used to be even more permissive than they are now! The remaining question is whether we should go further in restricting the roles.

@idontgetoutmuch, out of curiosity, which flavor of Vector were you coerce-ing under? (I'm not sure if it's the Prim- or Storable-based one.)

@ekmett
Copy link
Member

ekmett commented Oct 29, 2018

We could always offer a function ourself for the coercion, an unsafeCoerce if you will. ;)

@idontgetoutmuch
Copy link
Member Author

idontgetoutmuch commented Nov 8, 2018

@RyanGlScott
Copy link
Member

I see. Unfortunately, that's exactly the flavor of Vector that ought to be nominally roled, as I've now come to realize, which means that this code would stop working if we were to give it the role signature it really deserves :(

I think that if we are going to do this, we should follow @ekmett's advice and offer something like:

unsafeVectorCoerce :: Coercible a b => Storable.Vector a -> Storable.Vector b
unsafeVectorCoerce = unsafeCoerce

As the name suggests, it would still be unsound to use this in general, but if you do know what you're doing, then you can at least have some assurance that the type parameters are representationally equal.

@cartazio
Copy link
Contributor

cartazio commented Nov 8, 2018 via email

@idontgetoutmuch
Copy link
Member Author

@RyanGlScott Yes I feared as much when I looked at my code but I was beguiled by the the word "safe" in the documentation. Yes I should use unsafeVectorCoerce and presumably have a test such that should ghc ever change the representation for Double it will get caught.

UnkindPartition added a commit to UnkindPartition/hmatrix-sundials that referenced this pull request Nov 26, 2018
to avoid accidentally coercing vectors. See
haskell/vector#224.
@RyanGlScott
Copy link
Member

I've submitted #235 to make Storable vectors nominally roled.

@Shimuuar Shimuuar mentioned this pull request Jan 31, 2020
Shimuuar added a commit to Shimuuar/vector that referenced this pull request Jun 13, 2020
Reasoning is identical to one for storable vectors. See discussion in haskell#224, and
PR haskell#235.

Fixes haskell#277
Shimuuar added a commit to Shimuuar/vector that referenced this pull request Jun 14, 2020
Reasoning is identical to one for storable vectors. See discussion in haskell#224, and
PR haskell#235.

Fixes haskell#277
Shimuuar added a commit to Shimuuar/vector that referenced this pull request Jun 14, 2020
Reasoning is identical to one for storable vectors. See discussion in haskell#224, and
PR haskell#235.

Fixes haskell#277
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this pull request Aug 19, 2022
# Changes in version 0.13.0.0

 * `mkType` from `Data.Vector.Generic` is deprecated in favor of
   `Data.Data.mkNoRepType`
 * The role signatures on several `Vector` types were too permissive, so they
   have been tightened up:
   * The role signature for `Data.Vector.Mutable.MVector` is now
     `type role MVector nominal representational` (previously, both arguments
     were `phantom`). [#224](haskell/vector#224)
   * The role signature for `Data.Vector.Primitive.Vector` is now
     `type role Vector nominal` (previously, it was `phantom`).
     The role signature for `Data.Vector.Primitive.Mutable.MVector` is now
     `type role MVector nominal nominal` (previously, both arguments were
     `phantom`). [#316](haskell/vector#316)
   * The role signature for `Data.Vector.Storable.Vector` is now
     `type role Vector nominal` (previous, it was `phantom`), and the signature
     for `Data.Vector.Storable.Mutable.MVector` is now
     `type role MVector nominal nominal` (previous, both arguments were
     `phantom`). [#235](haskell/vector#235)

     We pick `nominal` for the role of the last argument instead of
     `representational` since the internal structure of a `Storable` vector is
     determined by the `Storable` instance of the element type, and it is not
     guaranteed that the `Storable` instances between two representationally
     equal types will preserve this internal structure.  One consequence of this
     choice is that it is no longer possible to `coerce` between
     `Storable.Vector a` and `Storable.Vector b` if `a` and `b` are nominally
     distinct but representationally equal types. We now provide
     `unsafeCoerce{M}Vector` and `unsafeCast` functions to allow this (the onus
     is on the user to ensure that no `Storable` invariants are broken when
     using these functions).
 * Methods of type classes `Data.Vector.Generic.Mutable.MVector` and
   `Data.Vector.Generic.Vector` use concrete monads (`ST`, etc) istead of being
   polymorphic (`PrimMonad`, etc). [#335](haskell/vector#335).
   This makes it possible to derive `Unbox` with:
   * `GeneralizedNewtypeDeriving`
   * via `UnboxViaPrim` and `Prim` instance
   * via `As` and `IsoUnbox` instance: [#378](haskell/vector#378)
 * Add `MonadFix` instance for boxed vectors: [#312](haskell/vector#312)
 * Re-export `PrimMonad` and `RealWorld` from mutable vectors:
   [#320](haskell/vector#320)
 * Add `maximumOn` and `minimumOn`: [#356](haskell/vector#356)
 * The functions `scanl1`, `scanl1'`, `scanr1`, and `scanr1'` for immutable
   vectors are now defined when given empty vectors as arguments,
   in which case they return empty vectors. This new behavior is consistent
   with the one of the corresponding functions in `Data.List`.
   Prior to this change, applying an empty vector to any of those functions
   resulted in an error. This change was introduced in:
   [#382](haskell/vector#382)
 * Change allocation strategy for `unfoldrN`: [#387](haskell/vector#387)
 * Remove `CPP` driven error reporting in favor of `HasCallStack`:
   [#397](haskell/vector#397)
 * Remove redundant `Storable` constraints on to/from `ForeignPtr` conversions:
   [#394](haskell/vector#394)
 * Add `unsafeCast` to `Primitive` vectors: [#401](haskell/vector#401)
 * Make `(!?)` operator strict: [#402](haskell/vector#402)
 * Add `readMaybe`: [#425](haskell/vector#425)
 * Add `groupBy` and `group` for `Data.Vector.Generic` and the specialized
   version in `Data.Vector`, `Data.Vector.Unboxed`, `Data.Vector.Storable` and
   `Data.Vector.Primitive`. [#427](haskell/vector#427)
 * Add `toArraySlice` and `unsafeFromArraySlice` functions for conversion to and
   from the underlying boxed `Array`: [#434](haskell/vector#434)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants