diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -11,6 +11,9 @@
 | 0.14.2 | 0.2.0.0 |
 | 0.14.3 | 0.3.0.0 |
 | 0.14.4 | 0.4.0.0 |
+| 0.14.5 | 0.4.0.0 |
+| 0.14.6 | 0.4.0.0 |
+| 0.14.7 | 0.5.0.0 |
 
 Before v0.14.2, there was a third package, `purescript-ast`. In v0.14.2, `purescript-ast` was merged into `purescript-cst`.
 
diff --git a/purescript-cst.cabal b/purescript-cst.cabal
--- a/purescript-cst.cabal
+++ b/purescript-cst.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               purescript-cst
-version:            0.4.0.0
+version:            0.5.0.0
 license:            BSD-3-Clause
 license-file:       LICENSE
 copyright:
@@ -83,18 +83,18 @@
     build-depends:
         aeson >=1.5.6.0 && <1.6,
         array >=0.5.4.0 && <0.6,
-        base >=4.14.1.0 && <4.15,
+        base >=4.14.3.0 && <4.15,
         base-compat >=0.11.2 && <0.12,
         bytestring >=0.10.12.0 && <0.11,
-        containers >=0.6.2.1 && <0.7,
+        containers >=0.6.5.1 && <0.7,
         deepseq >=1.4.4.0 && <1.5,
-        dlist >=0.8.0.8 && <0.9,
+        dlist ==1.0.*,
         filepath >=1.4.2.1 && <1.5,
-        microlens >=0.4.11.2 && <0.5,
+        microlens >=0.4.12.0 && <0.5,
         mtl >=2.2.2 && <2.3,
         protolude >=0.3.0 && <0.4,
-        scientific >=0.3.6.2 && <0.4,
-        semigroups >=0.19.1 && <0.20,
+        scientific >=0.3.7.0 && <0.4,
+        semigroups >=0.19.2 && <0.20,
+        serialise >=0.2.4.0 && <0.3,
         text >=1.2.4.1 && <1.3,
-        serialise >=0.2.3.0 && <0.3,
-        vector >=0.12.1.2 && <0.13
+        vector >=0.12.3.1 && <0.13
diff --git a/src/Language/PureScript/AST/Declarations.hs b/src/Language/PureScript/AST/Declarations.hs
--- a/src/Language/PureScript/AST/Declarations.hs
+++ b/src/Language/PureScript/AST/Declarations.hs
@@ -97,6 +97,7 @@
   | CheckHint
   | PositionHint
   | SolverHint
+  | DeclarationHint
   | OtherHint
   deriving (Show, Eq)
 
@@ -504,13 +505,13 @@
 declName (FixityDeclaration _ (Right (TypeFixity _ _ n))) = Just (TyOpName n)
 declName (TypeClassDeclaration _ n _ _ _ _) = Just (TyClassName n)
 declName (TypeInstanceDeclaration _ _ _ n _ _ _ _) = IdentName <$> hush n
+declName (RoleDeclaration RoleDeclarationData{..}) = Just (TyName rdeclIdent)
 declName ImportDeclaration{} = Nothing
 declName BindingGroupDeclaration{} = Nothing
 declName DataBindingGroupDeclaration{} = Nothing
 declName BoundValueDeclaration{} = Nothing
 declName KindDeclaration{} = Nothing
 declName TypeDeclaration{} = Nothing
-declName RoleDeclaration{} = Nothing
 
 -- |
 -- Test if a declaration is a value declaration
diff --git a/src/Language/PureScript/CST/Layout.hs b/src/Language/PureScript/CST/Layout.hs
--- a/src/Language/PureScript/CST/Layout.hs
+++ b/src/Language/PureScript/CST/Layout.hs
@@ -1,4 +1,11 @@
--- | The parser itself is unaware of indentation, and instead only parses explicit
+-- |
+-- ## High-Level Summary
+--
+-- This section provides a high-level summary of this file. For those who
+-- know more about compiler-development, the below explanation is likely enough.
+-- For everyone else, see the next section.
+--
+-- The parser itself is unaware of indentation, and instead only parses explicit
 -- delimiters which are inserted by this layout algorithm (much like Haskell).
 -- This is convenient because the actual grammar can be specified apart from the
 -- indentation rules. Haskell has a few problematic productions which make it
@@ -17,7 +24,148 @@
 -- such an algorithm. Unquoted properties for layout keywords introduce a domino
 -- effect of complexity since we have to mask and unmask any usage of . (also in
 -- foralls!) or labels in record literals.
-
+--
+-- ## Detailed Summary
+--
+-- ### The Problem
+--
+-- The parser itself is unaware of indentation or other such layout concerns.
+-- Rather than dealing with it explicitly, the parser and its
+-- grammar rules are only aware of normal tokens (e.g. @TokLowerName@) and
+-- three special zero-width tokens, @TokLayoutStart@, @TokLayoutSep@,
+-- and @TokLayoutEnd@. This is convenient because the actual grammar
+-- can be specified apart from the indentation rules and other such
+-- layout concerns.
+--
+-- For a simple example, the parser parses all three examples of the code below
+-- using the exact same grammar rules for the @let@ keyword despite
+-- each example using different indentations levels:
+--
+-- @
+-- -- Example 1
+-- let foo = 5
+--     x = 2 in foo
+--
+-- -- Example 2
+-- let
+--   bar = 5
+--   y = 2
+-- in bar
+--
+-- -- Example 3
+-- let        baz
+--                  =
+--              5
+--            z= 2 in baz
+-- @
+--
+-- Each block of code might appear to the parser as a stream of the
+-- following source tokens where the @\{@ sequence represents
+-- @TokLayoutStart@, the @\;@ sequence represents @TokLayoutSep@,
+-- and the @\}@ sequence represents @TokLayoutEnd@:
+-- - @let \{foo = 5\;x = 2\} in foo@
+-- - @let \{bar = 5\;y = 2\} in bar@
+-- - @let \{baz = 5\;z = 2\} in baz@
+--
+--
+-- For a more complex example, consider commas:
+--
+-- @
+-- case one, { twoA, twoB }, [ three1
+--    , three2
+--    ,   do
+--      { three3, three4 } <- case arg1, arg2 of
+--         Nothing, _ -> { three3: 1, three4: 2 }
+--         Just _, Nothing -> { three3: 2, three4: 3 }
+--         _, _ -> { three3: 3, three4: 4 }
+--      pure $ three3 + three4
+--    ] of
+-- @
+--
+-- Which of the above 13 commas function as the separaters between the
+-- case binders (e.g. @one@) in the outermost @case ... of@ context?
+--
+-- ### The Solution
+--
+-- The parser doesn't have to care about layout concerns (e.g. indentation
+-- or what starts and ends a context, such as a case binder) because the
+-- lexer solves that problem instead.
+--
+-- So, how does the lexer solve this problem? It follows this general algorithm:
+-- 1. Lex the source code text into an initial stream of `SourceToken`s
+--    that do not have any of the three special tokens mentioned previously.
+-- 2. On a token-by-token basis, determine whether the lexer should
+--        1. insert one of the three special tokens,
+--        2. modify the current context (e.g. are we within a case binder?
+--           Are we in a record expression?)
+--
+-- Step 2 is handled via 'insertLayout' and is essentially a state machine.
+-- The layout delimiters, (e.g. 'LytCase', 'LytBrace', 'LytProperty',
+-- and 'LytOf' in the next section's example) either stop certain "rules"
+-- from applying or ensure that certain "rules" now apply. By "rules",
+-- we mean whether and where one of the three special tokens are added.
+-- The comments in the source code for the 'insertLayout' algorithm call
+-- pushing these delimiters onto the stack "masking" and popping them off
+-- as "unmasking". Seeing when a layout delimiter is pushed and popped
+-- are the keys to understanding this algorithm.
+--
+-- ### Walking Through an Example
+--
+-- Before showing an example, let's remember a few things.
+--   1. The @TokLowerName "case"@ token (i.e. a "case" keyword) indicates the start
+--      of a @case ... of@ context. That context includes case binders (like the
+--      example shown previously) that can get quite complex. When encountered,
+--      we may need to insert one or more of the three special tokens here
+--      until we encounter the terminating @TokLowerName "of"@ token that
+--      signifies its end.
+--   2. "case" and "of" can also appear as a record field's name. In such a context,
+--      they would not start or end a @case ... of@ block.
+--
+-- Given the below source code...
+--
+-- @
+-- case { case: "foo", of: "bar" } of
+-- @
+--
+-- the lexer would go through something like the following states:
+-- 1. Encountered @TokLowerName "case"@. Update current context to
+--    "within a case of expression" by pushing the 'LytCase' delimiter
+--    onto the layout delimiter stack. Insert the @case@ token
+--    into the stream of source tokens.
+-- 2. Encountered @TokLeftBrace@. Update current context to
+--    "within a record expression" by pushing the 'LytBrace' delimiter.
+--    Since we expect a field name to be the next token we see,
+--    which may include a reserved keyword, update the current context again to
+--    "expecting a field name" by pushing the `LytProperty`.
+--    delimiter. Insert the @{@ token into the stream of source tokens.
+-- 3. Encountered @TokLowerName "case"@. Check the current context.
+--    Since it's a `LytProperty`, this is a field name and we shouldn't
+--    assume that the next few tokens will be case binders. However,
+--    since this might be a record with no more fields, update the
+--    current context back to "within a record expression" by popping
+--    the `LytProperty` off the layout delimiter stack. Insert the @case@ token
+-- 4. Encountered @TokColon@. Insert the @:@ token
+-- 5. Encountered @TokLowerName "foo"@. Insert the @foo@ token.
+-- 6. Encountered @TokComma@. Check the current context. Since it's a `LytBrace`,
+--    we're in a record expression and there is another field. Update the
+--    current context by pushing `LytProperty` as we expect a field name again.
+-- 7. Encountered @TokLowerName "of"@. Check the current context.
+--    Since it's a `LytProperty`, this is a field name rather
+--    than the end of a case binder. Thus, we don't expect the next tokens
+--    to be the @body@ in a @case ... of body@ expression. However, since
+--    this might be a record with no more fields, update the current context
+--    back to "within a record expression" by popping the `LytProperty`
+--    off the stack. Insert the @of@ token.
+-- 8. Encountered @TokRightBrace@. Check the current context.
+--    Since it's a `LytBrace`, this is the end of a record expression.
+--    Update the current context to "within a case of expression"
+--    by popping the `LytBrace` off the stack. Insert the @}@ token.
+-- 9. Encountered @TokLowername "of"@. Check the current context.
+--    Since it's a 'LytCase', this is the end of a @case ... of@ expression
+--    and the body will follow. Update the current context to
+--    "body of a case of expression" by pushing 'LytOf' onto the layout stack.
+--    Insert the @of@ token into the stream of tokens.
+--
 module Language.PureScript.CST.Layout where
 
 import Prelude
diff --git a/src/Language/PureScript/Environment.hs b/src/Language/PureScript/Environment.hs
--- a/src/Language/PureScript/Environment.hs
+++ b/src/Language/PureScript/Environment.hs
@@ -35,11 +35,6 @@
   , dataConstructors :: M.Map (Qualified (ProperName 'ConstructorName)) (DataDeclType, ProperName 'TypeName, SourceType, [Ident])
   -- ^ Data constructors currently in scope, along with their associated type
   -- constructor name, argument types and return type.
-  , roleDeclarations :: M.Map (Qualified (ProperName 'TypeName)) [Role]
-  -- ^ Explicit role declarations currently in scope. Note that this field is
-  -- only used to store declared roles temporarily until they can be checked;
-  -- to find a type's real checked and/or inferred roles, refer to the TypeKind
-  -- in the `types` field.
   , typeSynonyms :: M.Map (Qualified (ProperName 'TypeName)) ([(Text, Maybe SourceType)], SourceType)
   -- ^ Type synonyms currently in scope
   , typeClassDictionaries :: M.Map (Maybe ModuleName) (M.Map (Qualified (ProperName 'ClassName)) (M.Map (Qualified Ident) (NEL.NonEmpty NamedDict)))
@@ -103,7 +98,7 @@
 
 -- | The initial environment with no values and only the default javascript types defined
 initEnvironment :: Environment
-initEnvironment = Environment M.empty allPrimTypes M.empty M.empty M.empty M.empty allPrimClasses
+initEnvironment = Environment M.empty allPrimTypes M.empty M.empty M.empty allPrimClasses
 
 -- | A constructor for TypeClassData that computes which type class arguments are fully determined
 -- and argument covering sets.
@@ -434,7 +429,7 @@
     , (primSubName C.typeError "Fail", (kindDoc -:> kindConstraint, ExternData [Nominal]))
     , (primSubName C.typeError "Warn", (kindDoc -:> kindConstraint, ExternData [Nominal]))
     , (primSubName C.typeError "Text", (kindSymbol -:> kindDoc, ExternData [Phantom]))
-    , (primSubName C.typeError "Quote", (kindType -:> kindDoc, ExternData [Phantom]))
+    , (primSubName C.typeError "Quote", (tyForall "k" kindType $ tyVar "k" -:> kindDoc, ExternData [Phantom]))
     , (primSubName C.typeError "QuoteLabel", (kindSymbol -:> kindDoc, ExternData [Phantom]))
     , (primSubName C.typeError "Beside", (kindDoc -:> kindDoc -:> kindDoc, ExternData [Phantom, Phantom]))
     , (primSubName C.typeError "Above", (kindDoc -:> kindDoc -:> kindDoc, ExternData [Phantom, Phantom]))
diff --git a/src/Language/PureScript/Names.hs b/src/Language/PureScript/Names.hs
--- a/src/Language/PureScript/Names.hs
+++ b/src/Language/PureScript/Names.hs
@@ -78,11 +78,14 @@
 instance NFData Ident
 instance Serialise Ident
 
+unusedIdent :: Text
+unusedIdent = "$__unused"
+
 runIdent :: Ident -> Text
 runIdent (Ident i) = i
 runIdent (GenIdent Nothing n) = "$" <> T.pack (show n)
 runIdent (GenIdent (Just name) n) = "$" <> name <> T.pack (show n)
-runIdent UnusedIdent = "$__unused"
+runIdent UnusedIdent = unusedIdent
 
 showIdent :: Ident -> Text
 showIdent = runIdent
