Skip to content

Add type role annotations to Signal and DSignal #1640

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

Merged
merged 3 commits into from
Feb 4, 2021

Conversation

martijnbastiaan
Copy link
Member

@martijnbastiaan martijnbastiaan commented Feb 1, 2021

Prevents accidental, unsafe coerces

Prevents accidental, unsafe coerces
@martijnbastiaan martijnbastiaan marked this pull request as ready for review February 3, 2021 09:39
Copy link
Contributor

@alex-mckenna alex-mckenna left a comment

Choose a reason for hiding this comment

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

LGTM, do we also need a role annotation on BiSignal's signal types?

@martijnbastiaan
Copy link
Member Author

I believe so!

@christiaanb
Copy link
Member

Good question, what are the inferred roles of BiSignalIn and BiSignalOut now that Signal has a role annotation?

@alex-mckenna
Copy link
Contributor

In a similar vein, what about the Nat parameter on types like BitVector? I would assume these are also inferred as phantom when they should possibly also be nominal (if you want to change to a different size you should use a library function)

@alex-mckenna
Copy link
Contributor

Locally I get:

λ> :i BiSignalIn
type role BiSignalIn nominal nominal phantom

and for the sized types I get

λ> :i BitVector
type role BitVector phantom

λ> :i Index
type role Index phantom

λ> :i Signed
type role Signed phantom

λ> :i Unsigned
type role Unsigned phantom

λ> :i Vec
type role Vec nominal representational

Vec is fine because GADT parameters are always inferred as nominal IIRC, but I feel like the other sized types should have role annotations that the size of the type is nominal. Even if coerce is valid on them, coercions to different sizes are less surprising if only allowed through library functions like resize IMO

@martijnbastiaan
Copy link
Member Author

martijnbastiaan commented Feb 3, 2021

Even if coerce is valid on them, coercions to different sizes are less surprising if only allowed through library functions like resize IMO

Agreed. I'd like to put it a bit stronger; it's completely broken. E.g.,

>>> let x = coerce (4 :: Unsigned 3) :: Unsigned 2
>>> testBit x 0
False
>>> testBit x 1
False
>>> x == 0
False

Edit: Also, if this synthesizes, HDL simulation would yield different results.

@alex-mckenna
Copy link
Contributor

Even better, if it's completely broken we don't need to feel bad about changing it

@martijnbastiaan
Copy link
Member Author

@alex-mckenna Could you extend/add a changelog for the other fixes too? :)

BiSignal also needs role annotations to prevent the natural number
parameter from being treated as phantom. The natural number parameters
of sized types are also made nominal: changing the size of a sized
type should only be done explicitly using a well-defined resizing
function (like resize#).
@leonschoorl
Copy link
Member

Reset and Enable have phantom types too

Clash.Prelude Data.Coerce> :i Reset
type role Reset phantom
Clash.Prelude Data.Coerce> :i Enable
type role Enable phantom

@leonschoorl
Copy link
Member

Or do they inherit the nominal from Signal?

@martijnbastiaan
Copy link
Member Author

They should @leonschoorl, at least they did for BiSignalIn. Still, the explicitness might be nice.

@alex-mckenna
Copy link
Contributor

Or do they inherit the nominal from Signal?

I'll confirm now locally, but my understanding is that they go for the least restrictive option they can. So Clock using it's dom for a type where dom is nominal would mean it also has to be nominal in Clock (otherwise you could coerce clock and perform an illegal coercion on the underlying type)

@alex-mckenna
Copy link
Contributor

Yes:

λ> :i Clock
type role Clock nominal

@alex-mckenna
Copy link
Contributor

Still, the explicitness might be nice.

I disagree here, for most types the behaviour you want is the inherited option. If I see a role annotation my first thought is this must differ from the inferred roles which won't be the case. Less is more

@leonschoorl
Copy link
Member

Should this be backported to 1.2?
Or do we consider this an API change?

@martijnbastiaan
Copy link
Member Author

Yeah, it's an API change, in theory you could use it responsibly I guess.

I do think we should release 1.4 in a few weeks though.

@alex-mckenna
Copy link
Contributor

I wouldn't backport: this technically has the potential to break existing user code which uses coerce. As dodgy as this code is, I think that alone should exclude it from backporting. Related: maybe we should start maintaining a migrations guide as well?

@@ -156,6 +157,8 @@ import Clash.XException
>>> let n = $$(fLit pi) :: SFixed 4 4
-}

type role Fixed representational nominal nominal
Copy link
Member

@christiaanb christiaanb Feb 4, 2021

Choose a reason for hiding this comment

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

If this role annotation corresponds to the inferred role, could you remove it please.

@@ -69,6 +69,7 @@ operator that uses truncation introduces an additional error of /0.109375/:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RoleAnnotations #-}
Copy link
Member

Choose a reason for hiding this comment

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

Don't forget to remove this if you decide to remove the role annotation for Fixed

Type roles now feature in documentation, with the types in
Clash.Sized.* now recommending the use of the Resize class to
change the width of the type in a well-defined way.
@alex-mckenna
Copy link
Contributor

Rendered new docs:
Screenshot from 2021-02-04 11-34-54

types in Clash.Sized.* also recommend using Clash.Class.Resize.Resize to convert

Screenshot from 2021-02-04 11-35-32

@martijnbastiaan martijnbastiaan merged commit c99a766 into master Feb 4, 2021
@martijnbastiaan martijnbastiaan deleted the signal-type-roles branch February 4, 2021 13:02
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.

4 participants