diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,31 @@
 # Revision history for demangler
 
+## 1.3.2.0 -- 2023.04.22
+
+* Fix handling of discriminators.
+* Add handling for UnnamedTypeName unqualified names.
+* Fix parsing of primary expressions in template parameters.
+* Fully support DeclType elements.
+* More complete implementation of function params.
+* Fix parsing of reference qualifiers
+* Suppress function argument output for template arguments.
+* Allow extraction of function names with unnamed-type elements.
+* Convert full ClassUnionStructEnum name to a substitutable prefix.
+* Suppress any visible indication of empty template argument packs.
+* Add C11 argument pack expansion support with template argument packs.
+* Add support for pointer-to-member-type elements.
+* Proper UnscopedName parsing for template parameters.
+* Fixed order of CV qualifier parsing.
+
+## 1.3.1.0 -- 2023.11.12
+
+* Added support for demangling expressions.
+
+## 1.3.0.0 -- 2023.11.09
+
+* Added `functionName` accessor to retrieve the name portions only of a function
+  (in reverse heirarchical order).
+
 ## 1.2.0.0 -- 2023.10.29
 
 * Updated support for local names.
diff --git a/demangler.cabal b/demangler.cabal
--- a/demangler.cabal
+++ b/demangler.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               demangler
-version:            1.2.0.0
+version:            1.3.2.0
 synopsis:           Demangler for C++ mangled names.
 description:
    Provides a demangler (and mangler) for C++ names, with an intermediate
@@ -23,7 +23,9 @@
                     README.org
 
 extra-source-files: test/initial-test-cases.txt
+                    test/initial-test-cases.txt-funcnames
                     test/full-test-cases.txt
+                    test/full-test-cases.txt-funcnames
 
 tested-with: GHC == 8.8.4
            , GHC == 8.10.7
@@ -58,7 +60,8 @@
     hs-source-dirs:   src
     default-language: Haskell2010
     exposed-modules:  Demangler
-    other-modules:    Demangler.Context
+    other-modules:    Demangler.Accessors
+                      Demangler.Context
                       Demangler.Engine
                       Demangler.PPrint
                       Demangler.Structure
@@ -66,7 +69,7 @@
     build-depends:    base >=4.10 && < 5
                     , containers
                     , lens >= 5.0 && < 5.3
-                    , sayable >= 1.2.3.0 && < 1.3
+                    , sayable >= 1.2.3.1 && < 1.3
                     , template-haskell
                     , text
     if flag(debug)
diff --git a/src/Demangler.hs b/src/Demangler.hs
--- a/src/Demangler.hs
+++ b/src/Demangler.hs
@@ -21,6 +21,7 @@
   , Result
   , demangle
   , demangle1
+  , functionName
   )
 where
 
@@ -37,6 +38,7 @@
 import qualified Data.Text as T
 import           Text.Sayable
 
+import           Demangler.Accessors
 import           Demangler.Context
 import           Demangler.Engine
 import           Demangler.PPrint ()
@@ -149,7 +151,7 @@
              , unscoped_template_name >&=> template_args
                >=> rmap (uncurry UnscopedTemplateName)
              , local_name
-             , unscoped_name
+             , unscoped_name >=> rmap UnscopedName
              ]
 
 nested_name :: AnyNext NestedName
@@ -187,18 +189,16 @@
                      let (cvq, mb'refQual) = i ^. nVal
                      ret pa $ NestedTemplateName p a cvq mb'refQual
 
-unscoped_name :: AnyNext Name
+unscoped_name :: AnyNext UnscopedName
 unscoped_name =
-  asum' [ unqualified_name >=> rmap (UnscopedName False)
-        , match "St" >=> unqualified_name >=> rmap (UnscopedName True)
+  asum' [ unqualified_name >=> rmap (UnScName False)
+        , match "St" >=> unqualified_name >=> rmap (UnScName True)
         ]
 
 unscoped_template_name :: AnyNext Name
 unscoped_template_name i =
-  (unscoped_name i >>= canSubstUnscopedTemplateName)
-  <|> (substitution i
-        >>= substituteUnqualifiedName (rmap StdSubst)
-        >>= rmap (UnscopedName False)
+  (unscoped_name i >>= rmap UnscopedName >>= canSubstUnscopedTemplateName)
+  <|> (substitution i >>= (rmap UnscopedName <=< substituteUnscopedName (rmap UnScSubst))
       )
 
 
@@ -206,15 +206,10 @@
 local_name = match "Z"
              >=> function_encoding
              >=> match "E"
-             >=> asum' [ match "s"
-                         >=> rmap StringLitName
-                         >=> optional' discriminator
-                         >=> rmap (uncurry ($))
-                       , rmap LocalName
-                         >&=> name
-                         >=> rmap (uncurry ($))
-                         >=> optional' discriminator
-                         >=> rmap (uncurry ($))
+             >=> asum' [ match "s" >=> rmap StringLitName
+                         >=> optional' discriminator >=> rapply
+                       , rmap LocalName >&=> name >=> rapply
+                         >=> optional' discriminator >=> rapply
                        ]
 
 
@@ -224,13 +219,13 @@
 cv_qualifiers =
   let ifPresent v i = rmap (\(a,p) -> if isJust p then v:a else a) i
   in insert []
-     >=> optional' (match "K") >=> ifPresent Const_
-     >=> optional' (match "V") >=> ifPresent Volatile
      >=> optional' (match "r") >=> ifPresent Restrict
+     >=> optional' (match "V") >=> ifPresent Volatile
+     >=> optional' (match "K") >=> ifPresent Const_
 
 ref_qualifier :: AnyNext RefQualifier
-ref_qualifier = asum' [ match "&&" >=> rmap (const RefRef)
-                      , match "&" >=> rmap (const Ref)
+ref_qualifier = asum' [ match "O" >=> rmap (const RefRef)
+                      , match "R" >=> rmap (const Ref)
                       ]
 
 -- | Parse prefix.  This is a bit tricky though.  The BNF specifies:
@@ -321,12 +316,12 @@
 substitutionPrefixR = rmap (($ PrefixEnd) . PrefixUQName . StdSubst)
 
 decltype :: AnyNext DeclType
-decltype = asum' [ match "Dt" >=> expression >=> match "E" >=> tbd "decltype1"
-                 , match "DT" >=> expression >=> match "E" >=> tbd "decltype2"
+decltype = asum' [ match "Dt" >=> expression >=> match "E" >=> rmap DeclType
+                 , match "DT" >=> expression >=> match "E" >=> rmap DeclTypeExpr
                  ]
 
-closure_prefix :: AnyNext ClosurePrefix
-closure_prefix = tbd "closure prefix"
+-- closure_prefix :: AnyNext ClosurePrefix
+-- closure_prefix = tbd "closure prefix"
 
 unqualified_name :: AnyNext UnqualifiedName
 unqualified_name =
@@ -345,6 +340,7 @@
                  , source_name >&=> many' abi_tag . rdiscard
                    >=> rmap (uncurry SourceName)
                  , unnamed_type_name
+
                    -- , match "DC" i >>= some source_name >>= match "E"
                  ]
 
@@ -356,7 +352,7 @@
 
 operator_name :: AnyNext Operator
 operator_name =
-  let opMatch (o,(t,_)) = match t >=> rmap (const o)
+  let opMatch (o,(_,(t,_))) = match t >=> rmap (const o)
   in asum' ((opMatch <$> opTable)
              <> [ match "cv" >=> type_ >=> rmap OpCast
                 , match "li" >=> source_name >=> rmap OpString
@@ -396,7 +392,11 @@
 
 
 unnamed_type_name :: AnyNext UnqualifiedName
-unnamed_type_name = tbd "unnamed_type_name"
+unnamed_type_name = match "Ut"
+                    >=> ret' UnnamedTypeName
+                    >=> optional' digits_num >=> rmap (fmap $ fmap toEnum)
+                    >=> rapply
+                    >=> match "_"
 
 -- | Parse the function argument (and return) types for a function.
 --
@@ -429,13 +429,13 @@
                             Nothing ->
                               cannot Demangler "bare_function_type.withRetType"
                               [ "Function with rtype and no argtypes: "
-                              , sez @"error" (WC (i ^. nVal) (i ^. nContext))
+                              , sez @"error" (addContext (i ^. nVal) (i ^. nContext))
                               ]
      let noRetType = rmap (constr (i ^. nVal) Nothing) tys
      case i ^. nVal of
        FunctionName
          (UnscopedTemplateName
-           (UnscopedName _ (OperatorName (OpCast {}) _)) _) -> noRetType
+           (UnscopedName (UnScName _ (OperatorName (OpCast {}) _))) _) -> noRetType
        FunctionName (UnscopedTemplateName {}) -> withRetType
        FunctionName (NameNested (NestedTemplateName pr _ _ _)) ->
          case pr of
@@ -482,24 +482,24 @@
           rmap ((:|[]) . BaseType) <=< builtin_type
 
           -- Single element matches
-        , asum' [ qualified_type
-                , function_type
+        , asum' [ function_type
                 , class_enum_type
                 , array_type
-                  -- , pointer_to_member_type
+                , pointer_to_member_type
                 , template_template_param >&=> template_args
                   >=> rmap (uncurry Template)
-                  -- , decltype
+                , decltype >=> rmap DeclType_
                 -- This one is tricky: it's recursive, but then binds the
                 -- (possibly) multiple returned recursion types into a single
                 -- type.
-                , match "Dp" >=> type_parser >=> rmap Cpp11PackExpansion
+                , match "Dp" >=> types_ >=> rmap Cpp11PackExpansion
                 ]
           >=> canSubstType
           >=> rmap (:|[])
 
           -- Possibly multiple element matches (either direct or via recursion)
-        , asum' [ match "P" >=> type_parser >=> rmap (fmap Pointer)
+        , asum' [ qualified_type
+                , match "P" >=> type_parser >=> rmap (fmap Pointer)
                 , match "R" >=> type_parser >=> rmap (fmap LValRef)
                 , match "O" >=> type_parser >=> rmap (fmap RValRef)
                 , match "C" >=> type_parser >=> rmap (fmap ComplexPair)
@@ -521,7 +521,7 @@
                                 in ret i =<< NEL.nonEmpty (each <$> tas)
                               o -> cannot Demangler "type_parser.template_param"
                                    [ "bad template param ref in type:"
-                                   , sez @"debug" (WC o (i ^. nContext))
+                                   , sez @"debug" (addContext o (i ^. nContext))
                                    , "raw: " <> show o
                                    ]
                       )
@@ -550,21 +550,24 @@
           ]
      )
 
-qualified_type :: AnyNext Type_
+-- Returns potentially multiple types to support C++11 argument packs
+qualified_type :: AnyNext (NEL.NonEmpty Type_)
 qualified_type i = do eQ <- many' extended_qualifier $ rdiscard i
                       cQ <- cv_qualifiers eQ
                       -- Require some amount of production before recursion
                       require $ not $ and [ null $ cQ ^. nVal
                                           , null $ eQ ^. nVal
                                           ]
-                      tY <- type_ cQ
-                      ret tY $ QualifiedType (eQ ^. nVal) (cQ ^. nVal) (tY ^. nVal)
+                      tYs <- type_parser cQ
+                      let eQv = eQ ^. nVal
+                      let cQv = cQ ^. nVal
+                      let qTy = QualifiedType eQv cQv
+                      ret tYs (qTy <$> (tYs ^. nVal))
 
 extended_qualifier :: Next () ExtendedQualifier
-extended_qualifier = tbd "extended_qualifier"
+extended_qualifier = match "U" >=> tbd "extended_qualifier"
 
 function_type :: AnyNext Type_
--- function_type = tbd "function_type"
 function_type i = do f0 <- cv_qualifiers i
                            >>= optional' exception_spec
                            >>= optional' (match "Dx")
@@ -606,6 +609,11 @@
                          >=> rmap (uncurry (ArrayType . ExprBound))
                        ]
 
+pointer_to_member_type :: AnyNext Type_
+pointer_to_member_type = match "M"
+                         >=> type_ >=> rmap PointerToMember
+                         >&=> type_ >=> rapply
+
 function_encoding :: AnyNext Encoding
 function_encoding i = do e <- encoding i
                          require $ case e ^. nVal of
@@ -614,10 +622,11 @@
                                      _ -> False
                          return e
 
-discriminator :: Next a Int
+discriminator :: Next a Discriminator
 discriminator = asum' [ match "_" >=> single_digit_num
                       , match "__" >=> digits_num >=> match "_"
                       ]
+                >=> rmap (Discriminator . toEnum)
 
 template_prefix_and_args :: AnyNext (TemplatePrefix, Maybe TemplateArgs)
 template_prefix_and_args =
@@ -701,9 +710,12 @@
 
 template_arg :: AnyNext TemplateArg
 template_arg =
-  asum' [ type_ >=> rmap TArgType >=> canSubstTemplateArg
+  -- n.b. must check expr_primary ('L') before type_ because type_ can be a class
+  -- name, which can be a name, which can be an unqualified name, which can be a
+  -- "[module_name] L ..."
+  asum' [ expr_primary >=> rmap TArgSimpleExpr >=> canSubstTemplateArg
+        , type_ >=> rmap TArgType >=> canSubstTemplateArg
         , match "X" >=> expression >=> match "E" >=> rmap TArgExpr
-        , expression_primary >=> rmap TArgSimpleExpr >=> canSubstTemplateArg
         , match "J"
           >=> (\i -> do let locked = i ^. nTmplSubsLock
                         r <- many' template_arg . rdiscard $ i & nTmplSubsLock .~ True
@@ -721,14 +733,97 @@
   >=> substituteTemplateParam
 
 expression :: AnyNext Expression
-expression = asum' [ match "sp" >=> expression >=> rmap ExprPack
-                   , template_param >=> rmap ExprTemplateParam
-                   , expression_primary >=> rmap ExprPrim
-                   , tbd "expression"
-                   ]
+expression =
+  let opMatch (o,(Unary,(t,_))) = match t >=> expression >=> rmap (ExprUnary o)
+      opMatch (o,(Binary,(t,_))) = match t >=> expression
+                                   >=> rmap (ExprBinary o) >&=> expression
+                                   >=> rapply
+      opMatch (o,(Trinary,(t,_))) = match t >=> expression
+                                    >=> rmap (ExprTrinary o) >&=> expression
+                                    >=> rapply >&=> expression
+                                    >=> rapply
+      opMatch _ = const Nothing
+      binary_op = operator_name
+                  >=> \i -> case lookup (i^.nVal) opTable of
+                              Just (Binary, _) -> pure i
+                              _ -> Nothing
+      unary_op = operator_name
+                  >=> \i -> case lookup (i^.nVal) opTable of
+                              Just (Unary, _) -> pure i
+                              _ -> Nothing
+      rmap2 = rmap . uncurry
+      isGS = isJust . snd
+  in asum'
+     ((opMatch <$> opTable)
+      <>
+      [ match "pp_" >=> expression >=> rmap ExprPfxPlus
+      , match "mm_" >=> expression >=> rmap ExprPfxMinus
+      , match "cl" >=> some' expression >=> rmap ExprCall
+      , match "cv" >=> type_ >&=> expression
+        >=> match "E"  -- n.b. missing from https://itanium-cxx-abi.github.io
+        >=> rmap2 ExprConvert1
+      , match "cv" >=> type_ >=> match "_" >&=> some' expression
+        >=> rmap2 ExprConvertSome
+      , match "tl" >=> type_ >&=> many' braced_expression . rdiscard
+        >=> match "E" >=> rmap2 ExprConvertInit
+      , match "il" >=> many' braced_expression . rdiscard >=> rmap ExprBracedInit
+      , optional' (match "gs") >=> match "nw"
+        >&=> many' expression . rdiscard
+        >=> match "_" >&=> type_ >=> match "E"
+        >=> rmap2 (uncurry (ExprNew . isGS))
+      , optional' (match "gs") >=> match "nw" >=> rmap (ExprNewInit . isGS)
+        >&=> many' expression . rdiscard
+        >=> match "_" >&=> type_ >&=> initializer
+        >=> rmap2 (uncurry (uncurry ($)))
+      , optional' (match "gs") >=> match "na"
+        >&=> many' expression . rdiscard
+        >=> match "_" >&=> type_ >=> match "E"
+        >=> rmap2 (uncurry (ExprNewArray . isGS))
+      , optional' (match "gs") >=> match "na" >=> rmap (ExprNewInitArray . isGS)
+        >&=> many' expression . rdiscard
+        >=> match "_" >&=> type_ >&=> initializer
+        >=> rmap2 (uncurry (uncurry ($)))
+      , optional' (match "gs") >=> match "dl" >&=> expression
+        >=> rmap2 (ExprDel . isGS)
+      , optional' (match "gs") >=> match "da" >&=> expression
+        >=> rmap2 (ExprDelArray . isGS)
+      , match "dc" >=> type_ >&=> expression >=> rmap2 ExprDynamicCast
+      , match "sc" >=> type_ >&=> expression >=> rmap2 ExprStaticCast
+      , match "cc" >=> type_ >&=> expression >=> rmap2 ExprConstCast
+      , match "rc" >=> type_ >&=> expression >=> rmap2 ExprReinterpretCast
+      , match "ti" >=> type_ >=> rmap ExprTypeIdType
+      , match "te" >=> expression >=> rmap ExprTypeId
+      , match "st" >=> type_ >=> rmap ExprSizeOfType
+      , match "sz" >=> expression >=> rmap ExprSizeOf
+      , match "at" >=> type_ >=> rmap ExprAlignOfType
+      , match "az" >=> expression >=> rmap ExprAlignOf
+      , match "nx" >=> expression >=> rmap ExprNoException
+      , template_param >=> rmap ExprTemplateParam
+      , function_param >=> rmap ExprFunctionParam
+      , match "dt" >=> expression >&=> unresolved_name >=> rmap2 ExprField
+      , match "pt" >=> expression >&=> unresolved_name >=> rmap2 ExprFieldPtr
+      , match "ds" >=> expression >&=> expression >=> rmap2 ExprFieldExpr
+      , match "sZ" >=> template_param >=> rmap ExprSizeOfTmplParamPack
+      , match "sZ" >=> function_param >=> rmap ExprSizeOfFuncParamPack
+      , match "sP" >=> many' template_arg . rdiscard >=> match "E"
+        >=> rmap ExprSizeOfCapturedTmplParamPack
+      , match "sp" >=> expression >=> rmap ExprPack
+      , match "fl" >=> unary_op >&=> expression >=> rmap2 ExprUnaryLeftFold
+      , match "fr" >=> unary_op >&=> expression >=> rmap2 ExprUnaryRightFold
+      , match "fL" >=> binary_op >&=> expression >=> rmap2 ExprBinaryLeftFold
+        >&=> expression >=> rapply
+      , match "fR" >=> binary_op >&=> expression >=> rmap2 ExprBinaryRightFold
+        >&=> expression >=> rapply
+      , match "tw" >=> expression >=> rmap ExprThrow
+      , match "tr" >=> ret' ExprReThrow
+      , match "u" >=> source_name >&=> many' template_arg . rdiscard
+        >=> rmap2 ExprVendorExtended
+      , unresolved_name >=> rmap ExprUnresolvedName
+      , expr_primary >=> rmap ExprPrim
+      ])
 
-expression_primary :: AnyNext ExprPrimary
-expression_primary =
+expr_primary :: AnyNext ExprPrimary
+expr_primary =
   let toFloat w f = read (show w <> "." <> show f)
       floatLit ty w p = FloatLit (ty ^. nVal) $ toFloat (w ^.nVal) p
       complexLit ty w p iw ip =
@@ -751,3 +846,70 @@
     >=> asum' [ type_ >=> withType >=> match "E"
               , match "_Z" >=> encoding >=> match "E" >=> rmap ExternalNameLit
               ]
+
+braced_expression :: AnyNext BracedExpression
+braced_expression = asum' [ expression >=> rmap BracedExpr
+                          , match "di" >=> source_name >&=> braced_expression
+                            >=> rmap (uncurry BracedFieldExpr)
+                          , match "dx" >=> expression >&=> braced_expression
+                            >=> rmap (uncurry BracedIndexExpr)
+                          , match "dX" >=> expression >&=> expression
+                            >=> rmap (uncurry BracedRangedExpr)
+                            >&=> braced_expression >=> rapply
+                          ]
+
+initializer :: AnyNext InitializerExpr
+initializer = match "pi" >=> many' expression . rdiscard >=> match "E"
+              >=> rmap Initializer
+
+function_param :: AnyNext FunctionParam
+function_param = asum' [ match "fpT" >=> rmap (const FP_This)
+                       , match "fp" >=> cv_qualifiers >=> rmap FP_
+                         >=> match "_" >=> rmap ($ 1)
+                       , match "fp" >=> cv_qualifiers >=> rmap FP_
+                         >&=> (digits_num >=> rmap (toEnum . (+2)))
+                         >=> match "_" >=> rapply
+                       , match "fL" >=> tbd "function param l"
+                       ]
+
+unresolved_name :: AnyNext UnresolvedName
+unresolved_name =
+  asum' [ optional' (match "gs")
+          >&=> base_unresolved_name
+          >=> rmap (uncurry (URN_Base . isJust . snd))
+        , match "sr" >=> unresolved_type >&=> base_unresolved_name
+          >=> rmap (uncurry URNScopedRef)
+        , match "srN" >=> unresolved_type >=> rmap URNSubScopedRef
+          >&=> some' unresolved_qualifier_level >=> match "E" >=> rapply
+          >&=> base_unresolved_name >=> rapply
+        , optional' (match "gs")
+          >&=> match "sr" >=> rmap (URNQualRef . isJust . snd . fst)
+          >&=> some' unresolved_qualifier_level >=> match "E" >=> rapply
+          >&=> base_unresolved_name >=> rapply
+        ]
+
+base_unresolved_name :: AnyNext BaseUnresolvedName
+base_unresolved_name =
+  asum' [ source_name >=> optional' template_args >=> rmap (uncurry BUName)
+        , match "on" >=> operator_name >&=> template_args
+          >=> rmap (uncurry BUOnOperatorT)
+        , match "on" >=> operator_name >=> rmap BUOnOperator
+        , match "dn" >=> asum' [ unresolved_type
+                                 >=> rmap BUDestructorUnresolvedType
+                               , source_name >=> optional' template_args
+                                 >=> rmap (uncurry BUDestructorSimpleId)
+                               ]
+        ]
+
+unresolved_type :: AnyNext UnresolvedType
+unresolved_type i =
+  (asum' [ template_param >=> optional' template_args >=> rmap (uncurry URTTemplate)
+         , decltype >=> rmap URTDeclType
+         ]
+    i >>= canSubstUnresolvedType)
+  <|> (substitution i
+       >>= substituteUnresolvedType (tbd "substituteUnresolvedType"))
+
+unresolved_qualifier_level :: AnyNext UnresolvedQualifierLevel
+unresolved_qualifier_level =
+  source_name >=> optional' template_args >=> rmap (uncurry URQL)
diff --git a/src/Demangler/Accessors.hs b/src/Demangler/Accessors.hs
new file mode 100644
--- /dev/null
+++ b/src/Demangler/Accessors.hs
@@ -0,0 +1,111 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeApplications #-}
+
+module Demangler.Accessors
+  (
+    functionName
+  )
+where
+
+import           Data.List.NonEmpty ( NonEmpty( (:|) ) )
+import qualified Data.List.NonEmpty as NEL
+import           Data.Text ( Text )
+import qualified Data.Text as T
+
+import           Demangler.Context
+import           Demangler.Engine
+import           Demangler.Structure
+
+
+-- | Returns the base function name.  This is the core text name for the function
+-- (C-style) followed by the parent class/namespace (innermost-to-outermost) but
+-- without any argument and template information and therefore it is not
+-- necessarily unique.  The parent names have any template information removed as
+-- well. For example:
+--
+-- @std::map<int, char>::insert(...)@ returns @"insert" :| [ "map", "std" ]@
+--
+-- The reason for the reversed form is that the base name is usually the most
+-- relevant, and the parent information can be optionally consumed (and lazily
+-- generated) as needed.
+--
+-- If the name could not be demangled, the non-demangled form is returned
+-- (perhaps it is a plain function name already?).
+--
+-- If the demangled name is not a function (e.g. a data or special name) then
+-- Nothing is returned.
+
+functionName :: Result -> Maybe (NEL.NonEmpty Text)
+functionName (d,c) =
+  case d of
+    Original i -> Just $ contextStr (addContext () c) i :| []
+    Encoded e -> resolveCtorDtor <$> getEnc e
+    VendorExtended e _ -> getEnc e
+  where
+    resolveCtorDtor = \case
+      ("{{CTOR}" :| r@(nm : nm2 : _)) | "unnamed_type_num" `T.isPrefixOf` nm -> nm2 :| r
+      ("{{DTOR}" :| r@(nm : nm2 : _)) | "unnamed_type_num" `T.isPrefixOf` nm -> "~" <> nm2 :| r
+      ("{{CTOR}" :| r@(nm : _)) -> nm :| r
+      ("{{DTOR}" :| r@(nm : _)) -> "~" <> nm :| r
+      o -> o
+    getEnc = \case
+      EncFunc (FunctionName fn) _rty _argtys -> getName fn
+      EncStaticFunc (FunctionName fn) _rty _argtys -> getName fn
+      EncData (LocalName enc _ _) -> getEnc enc
+      _ -> Nothing
+    getName = \case
+      UnscopedName usn -> getUSN usn
+      UnscopedTemplateName nm _tmplArgs -> getName nm
+      NameNested nnm -> getNestedNm nnm
+      nm -> Just $ T.pack ( show nm ) :| []
+    getUSN = \case
+      UnScName False uqn -> Just $ NEL.fromList $ getUQN uqn
+      UnScName True uqn -> Just $ NEL.fromList $ getUQN uqn <> ["std"]
+      UnScSubst subs -> Just $ NEL.fromList $ getStdSubst subs
+    getUQN = \case
+      SourceName (SrcName i) _ -> [contextStr (addContext () c) i]
+      OperatorName op _ ->
+        [maybe (T.pack $ show op) (("operator" <>) . snd . snd)
+         $ lookup op opTable]
+      CtorDtorName ctd -> case ctd of
+                            CompleteCtor -> ["{{CTOR}"]
+                            BaseCtor -> ["{{CTOR}"]
+                            CompleteAllocatingCtor -> ["{{CTOR}"]
+                            CompleteInheritingCtor _ -> ["{{CTOR}"]
+                            BaseInheritingCtor _ -> ["{{CTOR}"]
+                            DeletingDtor -> ["{{DTOR}"]
+                            CompleteDtor -> ["{{DTOR}"]
+                            BaseDtor -> ["{{DTOR}"]
+      StdSubst sbst -> getStdSubst sbst
+      ModuleNamed _ uqn -> getUQN uqn
+      UnnamedTypeName mbnum ->
+        -- Highly unusual, and probably not ultimately useful.  This happens when
+        -- an unnamed structure/union/class has a function.  For example,
+        -- "_ZN3FooUt3_C2Ev" translates to "Foo::{unnamed type#5}::Foo()".
+        let n = maybe 1 (+2) mbnum in [ T.pack $ "unnamed_type_num" <> show n ]
+    getStdSubst = \case
+      SubStd -> ["std"]
+      SubAlloc -> [ "allocator", "std" ]
+      SubBasicString -> [ "basic_string", "std" ]
+      SubStdType BasicStringChar -> [ "string", "std" ]
+      SubStdType BasicIStream -> [ "istream", "std" ]
+      SubStdType BasicOStream -> [ "ostream", "std" ]
+      SubStdType BasicIOStream -> [ "iostream", "std" ]
+    getNestedNm = \case
+      NestedName pfx uqn _ _ -> NEL.nonEmpty $ getUQN uqn <> getPfx pfx
+      NestedTemplateName tmplpfx _tmplArgs _ _ -> NEL.nonEmpty $ getTmplPfx tmplpfx
+    getPfx = \case
+      PrefixTemplateParam _tmplParam r -> getPfxR r
+      PrefixDeclType _dclTy r -> getPfxR r
+      PrefixClosure _ -> []
+      Prefix r -> getPfxR r
+    getPfxR = \case
+      PrefixUQName uqn r -> getPfxR r <> getUQN uqn
+      PrefixTemplateArgs _ r -> getPfxR r
+      PrefixEnd -> []
+    getTmplPfx = \case
+      GlobalTemplate uqns -> foldr ((<>) . getUQN) [] uqns
+      NestedTemplate pfx uqns -> foldr ((<>) . getUQN) (getPfx pfx) uqns
+      TemplateTemplateParam _ -> []
diff --git a/src/Demangler/Context.hs b/src/Demangler/Context.hs
--- a/src/Demangler/Context.hs
+++ b/src/Demangler/Context.hs
@@ -6,7 +6,12 @@
   , newDemangling
   , contextFindOrAdd
   , contextStr
-  , WithContext(..)
+  , WithContext
+  , addContext
+  , withContext
+  , contextData
+  , withContextForTemplateArg
+  , isTemplateArgContext
   , sayableConstraints
   )
 where
@@ -46,10 +51,28 @@
     Just n -> (n, c)
     Nothing -> (Seq.length l, Context $ l |> s)
 
-contextStr :: Context -> Coord -> Text
-contextStr (Context l) i = l `Seq.index` i
+contextStr :: WithContext a -> Coord -> Text
+contextStr (WC _ _ (Context l)) i = l `Seq.index` i
 
-data WithContext a = WC a Context
+data SayingElement = DefaultSay | SayingTemplateArg
+
+data WithContext a = WC  SayingElement a Context
+
+addContext :: a -> Context -> WithContext a
+addContext = WC DefaultSay
+
+withContext :: WithContext a -> b -> WithContext b
+withContext (WC s _ c) d = WC s d c
+
+withContextForTemplateArg :: WithContext a -> b -> WithContext b
+withContextForTemplateArg (WC _ _ c) d = WC SayingTemplateArg d c
+
+isTemplateArgContext :: WithContext a -> Bool
+isTemplateArgContext (WC SayingTemplateArg _ _) = True
+isTemplateArgContext _ = False
+
+contextData :: WithContext a -> a
+contextData (WC _ d _) = d
 
 sayableConstraints :: TH.Name -> TH.PredQ
 sayableConstraints forTy = do
diff --git a/src/Demangler/Engine.hs b/src/Demangler/Engine.hs
--- a/src/Demangler/Engine.hs
+++ b/src/Demangler/Engine.hs
@@ -29,7 +29,7 @@
 
 instance PanicComponent Demangler where
   panicComponentName _ = "Demangler"
-  panicComponentIssues = const "https://github.com/galoisinc/demangler/issues"
+  panicComponentIssues = const "https://github.com/GaloisInc/demangler/issues"
   panicComponentRevision _ = ("main", "-")
 
 cannot :: PanicComponent a => a -> String -> [String] -> b
@@ -89,7 +89,10 @@
 rmap :: Applicative f => (a -> b) -> NextArg a -> f (NextArg b)
 rmap f i = ret i $ f (i ^. nVal)
 
+rapply :: Applicative f => NextArg (a -> b, a) -> f (NextArg b)
+rapply = rmap $ uncurry ($)
 
+
 --------------------
 -- Helpers
 
@@ -165,7 +168,7 @@
 a >&=> b = \i -> do x <- a i
                     y <- b x
                     rmap ((x ^. nVal,)) y
-infix 2 >&=>
+infixl 2 >&=>
 
 insert :: b -> Next a b
 insert v = pure . (nVal .~ v)
diff --git a/src/Demangler/PPrint.hs b/src/Demangler/PPrint.hs
--- a/src/Demangler/PPrint.hs
+++ b/src/Demangler/PPrint.hs
@@ -17,6 +17,7 @@
 
 module Demangler.PPrint () where
 
+import           Control.Applicative
 import           Data.Char
 import           Data.List.NonEmpty ( NonEmpty((:|)) )
 import qualified Data.List.NonEmpty as NEL
@@ -54,21 +55,21 @@
 
 $(return [])
 
-ctxLst :: forall saytag t a .
+ctxLst :: forall saytag t a b .
           Sayable saytag (WithContext a)
        => Functor t
        => Foldable t
-       => t a -> Context -> Saying saytag
-ctxLst l c = t'"" &+* wCtx l c
+       => t a -> WithContext b -> Saying saytag
+ctxLst l wc = t'"" &+* wCtx l wc
 
 ctxLst' :: Sayable saytag (WithContext a)
         => Functor t
         => Foldable t
-        => t a -> Context -> Text -> Saying saytag
-ctxLst' l c sep = sep &:* wCtx l c
+        => t a -> WithContext b -> Text -> Saying saytag
+ctxLst' l wc sep = sep &:* wCtx l wc
 
-wCtx :: Functor t => t a -> Context -> t (WithContext a)
-wCtx a c = (\b -> WC b c) <$> a
+wCtx :: Functor t => t a -> WithContext b -> t (WithContext a)
+wCtx a wc = withContext wc <$> a
 
 
 ----------------------------------------------------------------------
@@ -78,27 +79,27 @@
   ( Sayable "diagnostic" (WithContext Encoding)
   ) => Sayable "diagnostic" Result where
   sayable = \case
-    (Original i, c) -> contextStr c i &- t'"{orig}"
-    (Encoded e, c) -> sayable @"diagnostic" $ WC e c
+    (Original i, c) -> contextStr (addContext () c) i &- t'"{orig}"
+    (Encoded e, c) -> sayable @"diagnostic" $ addContext e c
     (VendorExtended d i, c) ->
-      let (s1,s2) = T.span isAlphaNum $ contextStr c i
-      in WC d c &- t'"[clone" &- s1 &+ ']' &+ s2
+      let (s1,s2) = T.span isAlphaNum $ contextStr (addContext () c) i
+      in addContext d c &- t'"[clone" &- s1 &+ ']' &+ s2
 
 instance {-# OVERLAPPABLE #-}
   ( Sayable saytag (WithContext Encoding)
   ) => Sayable saytag Result where
   sayable = \case
-    (Original i, c) -> sayable @saytag $ contextStr c i
-    (Encoded e, c) -> sayable @saytag $ WC e c
+    (Original i, c) -> sayable @saytag $ contextStr (addContext () c) i
+    (Encoded e, c) -> sayable @saytag $ addContext e c
     (VendorExtended d i, c) ->
-      let (s1,s2) = T.span isAlphaNum $ contextStr c i
-      in WC d c &- t'"[clone" &- '.' &+ s1 &+ ']' &+ s2
+      let (s1,s2) = T.span isAlphaNum $ contextStr (addContext () c) i
+      in addContext d c &- t'"[clone" &- '.' &+ s1 &+ ']' &+ s2
 
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''Encoding
   ) => Sayable saytag (WithContext Encoding) where
-  sayable (WC n c) =
-    case n of
+  sayable wc =
+    case contextData wc of
       -- Note: if the function has only a single void argument, print "()"
       -- instead of "(void)"; these are semantically the same, but demangling
       -- emits the former.
@@ -106,25 +107,32 @@
       -- Another tricky part is that the FunctionName may contain qualifiers
       -- (esp. "const") but for a function these must be placed at the end,
       -- following the arguments.
-      EncFunc f rty (BaseType Void :| []) -> sayFunction c f rty []
-      EncFunc f rty t -> sayFunction c f rty $ NEL.toList t
+      --
+      -- Additionally, if this function is being printed as part of a template
+      -- argument, then do not print the arguments.  This conforms to GCC c++filt
+      -- output, although llvm-cxxfilt *does* print the arguments, however, the
+      -- testing oracle is c++filt.
+      EncFunc f rty (BaseType Void :| []) -> sayFunction wc f rty []
+      EncFunc f rty t -> sayFunction wc f rty $ NEL.toList t
       -- n.b. static functions don't have any visible difference in demangled
       -- form.
-      EncStaticFunc f rty (BaseType Void :| []) -> sayFunction c f rty []
-      EncStaticFunc f rty t -> sayFunction c f rty $ NEL.toList t
-      EncConstStructData nm -> sayable @saytag $ WC nm c
-      EncData nm -> sayable @saytag $ WC nm c
-      EncSpecial sn -> sayable @saytag $ WC sn c
+      EncStaticFunc f rty (BaseType Void :| []) -> sayFunction wc f rty []
+      EncStaticFunc f rty t -> sayFunction wc f rty $ NEL.toList t
+      EncConstStructData nm -> sayable @saytag $ withContext wc nm
+      EncData nm -> sayable @saytag $ withContext wc nm
+      EncSpecial sn -> sayable @saytag $ withContext wc sn
 
 sayFunction :: Sayable saytag (WithContext Type_)
-            => Context -> FunctionName -> Maybe Type_ -> [Type_] -> Saying saytag
-sayFunction c fn mbRet args =
+            => WithContext a -> FunctionName -> Maybe Type_ -> [Type_] -> Saying saytag
+sayFunction wc fn mbRet args =
   let (nm,q) = cleanFunctionName fn
       part1 = case mbRet of
-                Nothing -> WC nm c &+ t'""
-                Just rty -> WC rty c &- WC nm c
-      part2 = part1 &+ '(' &+ ctxLst args c &+ ')'
-  in if null q then part2 else part2 &- ctxLst' q c " "
+                Nothing -> withContext wc nm &+ t'""
+                Just rty -> withContext wc rty &- withContext wc nm
+      part2 = if isTemplateArgContext wc
+              then part1
+              else part1 &+ '(' &+ ctxLst args wc &+ ')'
+  in if null q then part2 else part2 &- ctxLst' q wc " "
 
 instance Sayable saytag (WithContext a)
   => Sayable saytag (NonEmpty (WithContext a)) where
@@ -132,7 +140,7 @@
 
 instance {-# OVERLAPPABLE #-} Sayable saytag (WithContext a)
   => Sayable saytag (WithContext (NonEmpty a)) where
-  sayable (WC l c) = ctxLst l c
+  sayable wc = ctxLst (contextData wc) wc
 
 cleanFunctionName :: FunctionName -> (Name, [CVQualifier])
 cleanFunctionName (FunctionName nm) =
@@ -146,132 +154,178 @@
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''SpecialName
   ) => Sayable saytag (WithContext SpecialName) where
-  sayable (WC n c) =
-    case n of
-      VirtualTable ty -> t'"vtable for" &- WC ty c
-      TemplateParameterObj ta -> t'"template parameter object for" &- WC ta c
-      VTT ty -> t'"VTT for" &- WC ty c
-      TypeInfo ty -> t'"typeinfo for" &- WC ty c
-      TypeInfoName ty -> t'"typeinfo name for" &- WC ty c
+  sayable wc =
+    case contextData wc of
+      VirtualTable ty -> t'"vtable for" &- withContext wc ty
+      TemplateParameterObj ta -> t'"template parameter object for" &- withContext wc ta
+      VTT ty -> t'"VTT for" &- withContext wc ty
+      TypeInfo ty -> t'"typeinfo for" &- withContext wc ty
+      TypeInfoName ty -> t'"typeinfo name for" &- withContext wc ty
       CtorVTable _ -> t'"construction vtable for" &- t'"()"
-      Thunk (VirtualOffset _o1 _o2) enc -> t'"virtual thunk to" &- WC enc c
-      Thunk (NonVirtualOffset _o1) enc -> t'"non-virtual thunk to" &- WC enc c
+      Thunk (VirtualOffset _o1 _o2) enc -> t'"virtual thunk to" &- withContext wc enc
+      Thunk (NonVirtualOffset _o1) enc -> t'"non-virtual thunk to" &- withContext wc enc
 
 
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''FunctionName
   ) => Sayable saytag (WithContext FunctionName) where
-  sayable (WC n c) = sayable @saytag $ WC n c
+  sayable wc = let FunctionName n = contextData wc
+               in sayable @saytag $ withContext wc n
 
 
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''Name
   ) => Sayable saytag (WithContext Name) where
-  sayable (WC n c) =
-    case n of
-      NameNested nn -> sayable @saytag $ WC nn c
-      UnscopedName False uqn -> sayable @saytag $ WC uqn c
-      UnscopedName True uqn -> t'"std::" &+ WC uqn c
-      UnscopedTemplateName nn ta -> WC nn c &+ WC ta c
-      LocalName fs fe mbd -> WC fs c  &+ t'"::" &+ WC fe c &? wCtx mbd c -- ??
-      StringLitName fs mbd -> WC fs c &? wCtx mbd c -- ??
+  sayable wc =
+    case contextData wc of
+      NameNested nn -> sayable @saytag $ withContext wc nn
+      UnscopedName usn -> sayable @saytag $ withContext wc usn
+      UnscopedTemplateName nn ta -> withContext wc nn &+ withContext wc ta
+      LocalName fs fe _discr -> withContext wc fs &+ t'"::" &+ withContext wc fe -- Discriminators are invisible in demangled form
+      StringLitName fs _discr -> sayable @saytag $ withContext wc fs  -- Discriminators are invisible in demangled form
 
 
+-- Note: this should never actually be used, but the sayableConstraints template
+-- haskell production doesn't know that.
+instance Sayable saytag Discriminator where
+  sayable _ = sayable @saytag $ t'""
+instance Sayable saytag (WithContext Discriminator) where
+  sayable _ = sayable @saytag $ t'""
+
+instance {--# OVERLAPPABLE #-}
+  $(sayableConstraints ''UnscopedName
+   ) =>  Sayable saytag (WithContext UnscopedName) where
+  sayable wc =
+    case contextData wc of
+      UnScName False uqn -> sayable @saytag $ withContext wc uqn
+      UnScName True uqn -> t'"std::" &+ withContext wc uqn
+      UnScSubst subs -> sayable @saytag $ withContext wc subs
+
+
 instance {-# OVERLAPPABLE #-} Sayable saytag (WithContext Coord) where
-  sayable (WC i c) = sayable @saytag $ contextStr c i
+  sayable wc = sayable @saytag $ contextStr wc $ contextData wc
 
 
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''UnqualifiedName
   ) =>  Sayable saytag (WithContext UnqualifiedName) where
-  sayable (WC n c) =
-    case n of
-      SourceName i [] -> sayable @saytag $ WC i c
-      SourceName i tags -> WC i c &+ ctxLst' tags c ""
-      OperatorName op [] -> sayable @saytag $ WC op c
-      OperatorName op tags -> WC op c &+ ctxLst' tags c ""
-      CtorDtorName cd -> sayable @saytag $ WC cd c
-      StdSubst subs -> sayable @saytag $ WC subs c
-      ModuleNamed mn uqn -> ctxLst' mn c "" &+ WC uqn c
+  sayable wc =
+    case contextData wc of
+      SourceName i [] -> sayable @saytag $ withContext wc i
+      SourceName i tags -> withContext wc i &+ ctxLst' tags wc ""
+      OperatorName op [] -> t'"operator" &+ withContext wc op
+      OperatorName op tags -> t'"operator" &+ withContext wc op &+ ctxLst' tags wc ""
+      CtorDtorName cd -> sayable @saytag $ withContext wc cd
+      StdSubst subs -> sayable @saytag $ withContext wc subs
+      ModuleNamed mn uqn -> ctxLst' mn wc "" &+ withContext wc uqn
+      -- GCC c++filt style:
+      UnnamedTypeName Nothing -> t'"{unnamed type#1" &+ '}'
+      UnnamedTypeName (Just nt) -> t'"{unnamed type#" &+ nt + 2 &+ '}'
+      -- llvm-cxxfilt style:
+      -- UnnamedTypeName Nothing -> t'"'unnamed" &+ '\''
+      -- UnnamedTypeName (Just nt) -> t'"'unnamed" &+ nt &+ '\''
 
+
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''SourceName
    ) => Sayable saytag (WithContext SourceName) where
-  sayable (WC (SrcName i) c) = sayable @saytag $ contextStr c i
+  sayable wc = let SrcName i = contextData wc in sayable @saytag $ contextStr wc i
 
 
 instance {-# OVERLAPPABLE #-}
   ($(sayableConstraints ''PrefixUQN)
   , Sayable saytag (WithContext PrefixCDtor)
   ) =>  Sayable saytag (WithContext PrefixUQN) where
-  sayable (WC (PUC p n) c) =
+  sayable wc =
+    let PUC p n = contextData wc in
     case n of
-      CtorDtorName cd -> sayable @saytag $ WC (PCDC p cd) c
-      _ -> sayable @saytag $ WC n c
+      CtorDtorName cd -> sayable @saytag $ withContext wc (PCDC p cd)
+      _ -> sayable @saytag $ withContext wc n
 
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''ModuleName
   ) => Sayable saytag (WithContext ModuleName) where
-  sayable (WC (ModuleName isP sn) c) =
-    if isP
-    then WC sn c &+ ':'
-    else WC sn c &+ '.'
+  sayable wc =
+    let ModuleName isP sn = contextData wc
+    in if isP then withContext wc sn &+ ':' else withContext wc sn &+ '.'
 
 {- | Use Sayable (Prefix, CtorDtor, Context) instead, since CtorDtor needs to
    reproduce Prefix name. -}
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''CtorDtor
    ) =>  Sayable saytag (WithContext CtorDtor) where
-  sayable (WC n c) =
-    case n of
-      CompleteCtor -> 'c' &+ '1'
-      BaseCtor -> 'c' &+ '2'
-      CompleteAllocatingCtor -> 'c' &+ '3'
-      CompleteInheritingCtor t -> t'"ci1" &+ WC t c
-      BaseInheritingCtor t -> t'"ci2" &+ WC t c
-      DeletingDtor -> 'd' &+ '0'
-      CompleteDtor -> 'd' &+ '1'
-      BaseDtor -> 'd' &+ '2'
+  sayable wc =
+    case contextData wc of
+      CompleteCtor -> '(' &+ ')'
+      BaseCtor -> '(' &+ ')'
+      CompleteAllocatingCtor -> '(' &+ ')'
+      CompleteInheritingCtor t -> withContext wc t &+ '(' &+ ')' -- ?
+      BaseInheritingCtor t -> withContext wc t &+ '(' &+ ')' -- ?
+      DeletingDtor -> '~' &+ '(' &+ ')'
+      CompleteDtor -> '~' &+ '(' &+ ')'
+      BaseDtor -> '~' &+ '(' &+ ')'
 
 instance {-# OVERLAPPABLE #-}
-  $(sayableConstraints ''PrefixCDtor
+  ($(sayableConstraints ''PrefixCDtor)
   ) =>  Sayable saytag (WithContext PrefixCDtor) where
-  sayable (WC (PCDC p n) c) =
-    let mb'ln = case p of
+  sayable wc =
+    let PCDC p n = contextData wc
+        mb'ln = case p of
                   Prefix pfxr -> pfxrLastUQName pfxr
                   _ -> cannot Demangler "sayable"
                        [ "CTORDTOR UNK PFX: " <> show p ]
         pfxrLastUQName = \case
+          PrefixUQName (UnnamedTypeName _) PrefixEnd -> Nothing
+          PrefixUQName (UnnamedTypeName _) (PrefixTemplateArgs _ PrefixEnd) -> Nothing
           PrefixUQName unm PrefixEnd -> Just unm
           PrefixUQName unm (PrefixTemplateArgs _ PrefixEnd) -> Just unm
-          PrefixUQName _ sp -> pfxrLastUQName sp
+          PrefixUQName unm sp -> pfxrLastUQName sp <|> Just unm  -- [note UTC]
           PrefixTemplateArgs _ sp -> pfxrLastUQName sp
           PrefixEnd -> Nothing
     in case mb'ln of
          Just ln ->
            case n of
-             CompleteCtor -> sayable @saytag $ WC ln c
-             BaseCtor -> sayable @saytag $ WC ln c
-             CompleteAllocatingCtor -> sayable @saytag $ WC ln c
-             CompleteInheritingCtor t -> sayable @saytag $ WC t c -- ??
-             BaseInheritingCtor t -> sayable @saytag $ WC t c -- ??
-             DeletingDtor -> '~' &+ WC ln c
-             CompleteDtor -> '~' &+ WC ln c
-             BaseDtor -> '~' &+ WC ln c
-         Nothing -> t'"unk_" &+ WC n c -- unlikely... and will be wrong
+             CompleteCtor -> sayable @saytag $ withContext wc ln
+             BaseCtor -> sayable @saytag $ withContext wc ln
+             CompleteAllocatingCtor -> sayable @saytag $ withContext wc ln
+             CompleteInheritingCtor t -> sayable @saytag $ withContext wc t -- ??
+             BaseInheritingCtor t -> sayable @saytag $ withContext wc t -- ??
+             DeletingDtor -> '~' &+ withContext wc ln
+             CompleteDtor -> '~' &+ withContext wc ln
+             BaseDtor -> '~' &+ withContext wc ln
+         Nothing -> sayable @saytag $ withContext wc n
 
+-- [Note UTC:] When printing a constructor or destructor that includes an
+-- UnnamedTypeName, there are differences between GCC's c++filt and LLVM's
+-- llvm-cxxfilt.  We adopt the former because the demangle tests use c++filt as
+-- an oracle, but it is possible to switch to the LLVM style (at compile time) by
+-- removing the alternative return of Just unm from the indicated
+--
+-- Example:
+--    _ZN3FooUt3_C2Ev is the base constructor for the 5th unnamed type
+--    in the Foo namespace.
+--
+--      c++filt _ZN3FooUt3_C2Ev -------> Foo::{unnamed type#5}::Foo()
+--      llvm-cxxfilt _ZN3FooUt3_C2Ev --> Foo::'unnamed3'::()'
 
+
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''Operator
   ) =>  Sayable saytag (WithContext Operator) where
-  sayable (WC op c) =
+  sayable wc =
+    let op = contextData wc in
     case lookup op opTable of
-      Just (_, o) -> t'"operator" &+ o
+      Just (_, (_, o)) -> sayable @saytag o
       Nothing ->
+        -- TODO: if these are printed as part of an expression rather than an
+        -- UnqualifiedName, the prefix space will be wrong (the latter prints
+        -- "operator" to name the function whereas the former just prints the
+        -- operation).  If this is an issue, probably needs to be a flag in
+        -- WithContext to indicate if this is an expression or not.
         case op of
-          OpCast ty -> t'"operator" &- WC ty c
-          OpString snm -> sayable @saytag $ WC snm c
-          OpVendor n snm -> t'"vendor" &- n &- WC snm c
+          OpCast ty -> ' ' &+ withContext wc ty
+          OpString snm -> ' ' &+ withContext wc snm
+          OpVendor n snm -> t'"vendor" &- n &- withContext wc snm  -- ?
           _ -> cannotSay Demangler "sayable"
                [ "Operator not in opTable or with a specific override:"
                , show op
@@ -281,47 +335,48 @@
   ($(sayableConstraints ''NestedName)
   , Sayable saytag (WithContext PrefixCDtor)
   ) => Sayable saytag (WithContext NestedName) where
-  sayable (WC n c) =
-    let qualrefs q r = ctxLst' q c " " &? wCtx r c
-    in case n of
+  sayable wc =
+    let qualrefs q r = ctxLst' q wc " " &? wCtx r wc
+    in case contextData wc of
       NestedName p (CtorDtorName nm) q r ->
-        qualrefs q r &+ WC p c &+ t'"::" &+ WC (PCDC p nm) c
-      NestedName EmptyPrefix nm q r -> qualrefs q r &+ WC nm c
-      NestedName p nm q r -> qualrefs q r &+ WC p c &+ t'"::" &+ WC nm c
-      NestedTemplateName tp ta q r -> qualrefs q r &+ WC tp c &+ WC ta c
+        qualrefs q r &+ withContext wc p &+ t'"::" &+ withContext wc (PCDC p nm)
+      NestedName EmptyPrefix nm q r -> qualrefs q r &+ withContext wc nm
+      NestedName p nm q r -> qualrefs q r &+ withContext wc p &+ t'"::" &+ withContext wc nm
+      NestedTemplateName tp ta q r -> qualrefs q r &+ withContext wc tp &+ withContext wc ta
 
 
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''Prefix
   ) => Sayable saytag (WithContext Prefix) where
-  sayable (WC p c) =
-    case p of
-      PrefixTemplateParam tp prefixr -> WC tp c &+ WC prefixr c
-      PrefixDeclType dt prefixr -> WC dt c &+ WC prefixr c
-      PrefixClosure cp -> sayable @saytag $ WC cp c -- ??
-      Prefix prefixr -> sayable @saytag $ WC prefixr c
+  sayable wc =
+    case contextData wc of
+      PrefixTemplateParam tp prefixr -> withContext wc tp &+ withContext wc prefixr
+      PrefixDeclType dt prefixr -> withContext wc dt &+ withContext wc prefixr
+      PrefixClosure cp -> sayable @saytag $ withContext wc cp -- ??
+      Prefix prefixr -> sayable @saytag $ withContext wc prefixr
 
+
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''PrefixR
   ) => Sayable saytag (WithContext PrefixR) where
-  sayable (WC p c) =
-    case p of
-      PrefixUQName uqn pfr@(PrefixUQName {}) -> WC uqn c &+ t'"::" &+ WC pfr c
-      PrefixUQName uqn pfr -> WC uqn c &+ WC pfr c
-      PrefixTemplateArgs ta pfr -> WC ta c &+ WC pfr c
+  sayable wc =
+    case contextData wc of
+      PrefixUQName uqn pfr@(PrefixUQName {}) -> withContext wc uqn &+ t'"::" &+ withContext wc pfr
+      PrefixUQName uqn pfr -> withContext wc uqn &+ withContext wc pfr
+      PrefixTemplateArgs ta pfr -> withContext wc ta &+ withContext wc pfr
       PrefixEnd -> sayable @saytag $ t'""
 
 
 instance {-# OVERLAPPABLE #-} Sayable saytag (WithContext CVQualifier) where
-  sayable (WC q _c) =
-    case q of
+  sayable wc =
+    case contextData wc of
       Restrict -> sayable @saytag $ t'"restrict"
       Volatile -> sayable @saytag $ t'"volatile"
       Const_ -> sayable @saytag $ t'"const"
 
 instance {-# OVERLAPPABLE #-} Sayable saytag (WithContext RefQualifier) where
-  sayable (WC q _c) =
-    case q of
+  sayable wc =
+    case contextData wc of
       Ref -> sayable @saytag '&'
       RefRef -> sayable @saytag $ t'"&&"
 
@@ -329,18 +384,22 @@
   ($(sayableConstraints ''TemplatePrefix)
   , Sayable saytag (WithContext PrefixUQN)
   ) => Sayable saytag (WithContext TemplatePrefix) where
-  sayable (WC p c) =
-    case p of
-      GlobalTemplate uqns -> ctxLst' uqns c "::"
-      NestedTemplate pr uqns -> WC pr c &+ t'"::"
-                                &+ ctxLst' (PUC pr <$> uqns) c "::"
-      TemplateTemplateParam tp -> sayable @saytag $ WC tp c
+  sayable wc =
+    case contextData wc of
+      GlobalTemplate uqns -> ctxLst' uqns wc "::"
+      NestedTemplate pr uqns -> withContext wc pr &+ t'"::"
+                                &+ ctxLst' (PUC pr <$> uqns) wc "::"
+      TemplateTemplateParam tp -> sayable @saytag $ withContext wc tp
 
 
 instance {-# OVERLAPPABLE #-}
   (Sayable saytag (WithContext TemplateArg)
   ) => Sayable saytag (WithContext TemplateArgs) where
-  sayable (WC args c) = '<' &+ ctxLst args c &+ templateArgsEnd args
+  sayable wc = let args = contextData wc
+                   args' = filter (not . emptyArgPack) $ NEL.toList args
+                   emptyArgPack (TArgPack []) = True
+                   emptyArgPack _ = False
+               in '<' &+ ctxLst args' wc &+ templateArgsEnd args
 
 -- C++ requires a space between template argument closures to resolve the parsing
 -- ambiguity between that and a right shift operation.(e.g. "list<foo<int> >"
@@ -362,13 +421,13 @@
                         _ -> ">"
 
 instance {-# OVERLAPPABLE #-}
-  $(sayableConstraints ''TemplateArg
+  ($(sayableConstraints ''TemplateArg)
   ) => Sayable saytag (WithContext TemplateArg) where
-  sayable (WC p c) =
-    case p of
-      TArgType ty -> sayable @saytag $ WC ty c
-      TArgSimpleExpr ep -> sayable @saytag $ WC ep c
-      TArgExpr expr -> sayable @saytag $ WC expr c
+  sayable wc =
+    case contextData wc of
+      TArgType ty -> sayable @saytag $ withContextForTemplateArg wc ty
+      TArgSimpleExpr ep -> sayable @saytag $ withContextForTemplateArg wc ep
+      TArgExpr expr -> sayable @saytag $ withContextForTemplateArg wc expr
       TArgPack tas ->
         -- Expected some ellipses (see
         -- https://en.cppreference.com/w/cpp/language/parameter-pack), but
@@ -379,23 +438,79 @@
         --
         -- Do not simply defer to the TemplateArgs sayable because that will
         -- engender another pair of surrounding angle brackets.
-        ctxLst tas c
+        ctxLst tas $ withContextForTemplateArg wc ()
 
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''Expression
   ) => Sayable saytag (WithContext Expression) where
-  sayable (WC e c) =
-    case e of
-      ExprPack expr -> sayable @saytag $ WC expr c
-      ExprTemplateParam tp -> sayable @saytag $ WC tp c
-      ExprPrim pe -> sayable @saytag $ WC pe c
-
+  sayable wc =
+    case contextData wc of
+      ExprUnary op expr -> withContext wc op &+ withContext wc expr
+      ExprBinary op expr1 expr2 -> withContext wc expr1 &+ withContext wc op &+ withContext wc expr2
+      ExprTrinary op expr1 expr2 expr3 ->
+        '(' &+ withContext wc expr1 &+ ')' &+ withContext wc op &+ withContext wc expr2 &- ':' &- withContext wc expr3
+      ExprPfxPlus expr -> t'"++" &+ withContext wc expr
+      ExprPfxMinus expr -> t'"--" &+ withContext wc expr
+      ExprCall (exprc :| args) -> withContext wc exprc &+ '(' &+ ctxLst args wc &+ ')'
+      ExprConvert1 ty expr -> '(' &+ withContext wc ty &+ ')' &+ withContext wc expr
+      ExprConvertSome ty exprs -> '(' &+ withContext wc ty &+ t'")(" &+ ctxLst exprs wc &+ ')'
+      ExprConvertInit ty brexprs -> withContext wc ty &+ '{' &+ ctxLst brexprs wc &+ '}'
+      ExprBracedInit brexprs -> '{' &+ ctxLst brexprs wc &+ '}'
+      ExprNew gs exprs ty -> t'"new (" &+ ctxLst exprs wc &+ ')'
+                             &- (if gs then "::" else t'"") &+ withContext wc ty
+      ExprNewInit gs exprs ty i -> t'"new (" &+ ctxLst exprs wc &+ ')'
+                                   &- (if gs then "::" else t'"") &+ withContext wc ty
+                                   &- '(' &+ withContext wc i &+ ')'
+      ExprNewArray gs exprs ty -> t'"new[] (" &+ ctxLst exprs wc &+ ')'
+                                  &- (if gs then "::" else t'"") &+ withContext wc ty
+      ExprNewInitArray gs exprs ty i -> t'"new[] (" &+ ctxLst exprs wc &+ ')'
+                                        &- (if gs then "::" else t'"") &+ withContext wc ty
+                                        &- '(' &+ withContext wc i &+ ')'
+      ExprDel gs expr -> t'"delete" &- (if gs then "::" else t'"") -- ??
+                         &+ withContext wc expr
+      ExprDelArray gs expr -> t'"delete[]" &- (if gs then "::" else t'"") -- ??
+                              &+ withContext wc expr
+      ExprDynamicCast ty expr -> t'"dynamic_cast<" &+ withContext wc ty &+ t'">("
+                                 &+ withContext wc expr &+ ')'
+      ExprStaticCast ty expr -> t'"static_cast<" &+ withContext wc ty &+ t'">("
+                                &+ withContext wc expr &+ ')'
+      ExprConstCast ty expr -> t'"const_cast<" &+ withContext wc ty &+ t'">("
+                               &+ withContext wc expr &+ ')'
+      ExprReinterpretCast ty expr -> t'"reinterpret_cast<" &+ withContext wc ty &+ t'">("
+                                     &+ withContext wc expr &+ ')'
+      ExprTypeIdType ty -> t'"typeid(" &+ withContext wc ty &+ ')'
+      ExprTypeId expr -> t'"typeid(" &+ withContext wc expr &+ ')'
+      ExprSizeOfType ty -> t'"sizeof(" &+ withContext wc ty &+ ')'
+      ExprSizeOf expr -> t'"sizeof(" &+ withContext wc expr &+ ')'
+      ExprAlignOfType ty -> t'"alignof(" &+ withContext wc ty &+ ')'
+      ExprAlignOf expr -> t'"alignof(" &+ withContext wc expr &+ ')'
+      ExprNoException expr -> t'"noexcept(" &+ withContext wc expr &+ ')'
+      ExprTemplateParam tp -> sayable @saytag $ withContext wc tp
+      ExprFunctionParam fp -> sayable @saytag $ withContext wc fp
+      ExprField expr urn -> withContext wc expr &+ '.' &+ withContext wc urn
+      ExprFieldPtr expr urn -> withContext wc expr &+ t'"->" &+ withContext wc urn
+      ExprFieldExpr baseExp fieldExp -> withContext wc baseExp &+ t'".*" &+ withContext wc fieldExp
+      ExprSizeOfTmplParamPack tp -> t'"sizeof...(" &+ withContext wc tp &+ ')'
+      ExprSizeOfFuncParamPack fp -> t'"sizeof...(" &+ withContext wc fp &+ ')'
+      ExprSizeOfCapturedTmplParamPack tas -> t'"sizeof...(" &+ ctxLst tas wc &+ ')'
+      ExprPack expr -> withContext wc expr &+ t'"..."
+      ExprUnaryLeftFold op expr  -> '(' &+ t'"..." &- withContext wc op &- withContext wc expr &+ ')'
+      ExprUnaryRightFold op expr -> '(' &+ withContext wc expr &- withContext wc op &- t'"..." &+ ')'
+      ExprBinaryLeftFold op exprL exprR  ->
+        '(' &+ withContext wc exprL &- withContext wc op &- t'"..." &- withContext wc op &- withContext wc exprR &+ ')'
+      ExprBinaryRightFold op exprL exprR ->
+        '(' &+ withContext wc exprL &- withContext wc op &- t'"..." &- withContext wc op &- withContext wc exprR &+ ')'
+      ExprThrow expr -> t'"throw" &- withContext wc expr
+      ExprReThrow -> sayable @saytag $ t'"throw"
+      ExprVendorExtended sn tas -> withContext wc sn &+ '<' &+ ctxLst tas wc &+ '>'
+      ExprUnresolvedName urn -> sayable @saytag $ withContext wc urn
+      ExprPrim pe -> sayable @saytag $ withContext wc pe
 
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''ExprPrimary
   ) => Sayable saytag (WithContext ExprPrimary) where
-  sayable (WC e c) =
-    case e of
+  sayable wc =
+    case contextData wc of
       IntLit ty n ->
         -- Normally these are printed with a typecast (e.g. `(type)`) ".
         -- However, C and C++ have some special situations where they can use
@@ -404,36 +519,124 @@
         case ty of
           BaseType Bool_ -> t'"" &+ if n > 0 then t'"true" else t'"false"
           BaseType bty -> case lookup bty builtinTypeTable of
-                            Just (_, cst, sfx) -> if T.null sfx
-                                                  then '(' &+ cst &+ ')' &+ n
-                                                  else n &+ sfx
-                            _ -> '(' &+ WC ty c &+ ')' &+ n
-          _ -> '(' &+ WC ty c &+ ')' &+ n
-      FloatLit ty n -> '(' &+ WC ty c &+ ')' &+ n
-      ComplexFloatLit ty r i -> '(' &+ WC ty c &+ ')' &+ '(' &+ r &+ ',' &- i &+ ')'
-      DirectLit ty -> '(' &+ WC ty c &+ t'")NULL"  -- except String?
-      NullPtrTemplateArg ty -> '(' &+ WC ty c &+ t'")0"
-      ExternalNameLit enc -> sayable @saytag $ WC enc c
+                            Just (_, _, Just sfx) -> n &+ sfx
+                            Just (_, cst, Nothing) -> '(' &+ cst &+ ')' &+ n
+                            _ -> '(' &+ withContext wc ty &+ ')' &+ n
+          _ -> '(' &+ withContext wc ty &+ ')' &+ n
+      FloatLit ty n -> '(' &+ withContext wc ty &+ ')' &+ n
+      ComplexFloatLit ty r i -> '(' &+ withContext wc ty &+ ')' &+ '(' &+ r &+ ',' &- i &+ ')'
+      DirectLit ty -> '(' &+ withContext wc ty &+ t'")NULL"  -- except String?
+      NullPtrTemplateArg ty -> '(' &+ withContext wc ty &+ t'")0"
+      ExternalNameLit enc -> sayable @saytag $ withContext wc enc
 
 
+instance {-# OVERLAPPABLE #-}
+  $(sayableConstraints ''BracedExpression
+   ) => Sayable saytag (WithContext BracedExpression) where
+  sayable wc =
+    case contextData wc of
+      BracedExpr e -> sayable @saytag $ withContext wc e
+      BracedFieldExpr sn be' -> '.' &+ withContext wc sn &+ withContext wc be'
+      BracedIndexExpr ixe be' ->
+        '[' &+ withContext wc ixe &+ ']' &+ '=' &+ '(' &+ withContext wc be' &+ ')'
+      BracedRangedExpr sr er be' -> '[' &+ withContext wc sr &- t'"..." &- withContext wc er &+ ']'
+                                    &- '=' &- withContext wc be'
+
+
+instance {-# OVERLAPPABLE #-}
+  $(sayableConstraints ''InitializerExpr
+   ) => Sayable saytag (WithContext InitializerExpr) where
+  sayable wc = let Initializer ies = contextData wc in ctxLst ies wc
+
+
+instance {-# OVERLAPPABLE #-}
+  $(sayableConstraints ''FunctionParam
+   ) => Sayable saytag (WithContext FunctionParam) where
+  sayable wc =
+    case contextData wc of
+      FP_This -> sayable @saytag $ t'"this"
+      FP_ cvq n -> sayable @saytag $ '{' &+ ctxLst' cvq wc " " &+ t'"parm#" &+ n &+ '}'
+
+
+instance {-# OVERLAPPABLE #-}
+  $(sayableConstraints ''UnresolvedName
+   ) => Sayable saytag (WithContext UnresolvedName) where
+  sayable wc =
+    case contextData wc of
+      URN_Base True burn -> t'"::" &+ withContext wc burn
+      URN_Base False burn -> sayable @saytag $ withContext wc burn
+      URNScopedRef urt burn -> withContext wc urt &+ t'"::" &+ withContext wc burn
+      URNSubScopedRef urt urqls burn -> withContext wc urt &+ t'"::"
+                                        &+ ctxLst' urqls wc (t'"::")
+                                        &+ t'"::" &+ withContext wc burn
+      URNQualRef True urqls burn -> t'"::"
+                                    &+ ctxLst' urqls wc (t'"::")
+                                    &+ t'"::" &+ withContext wc burn
+      URNQualRef False urqls burn -> ctxLst' urqls wc (t'"::")
+                                     &+ t'"::" &+ withContext wc burn
+
+
+instance {-# OVERLAPPABLE #-}
+  $(sayableConstraints ''BaseUnresolvedName
+   ) => Sayable saytag (WithContext BaseUnresolvedName) where
+  sayable wc =
+    case contextData wc of
+      BUName sn Nothing -> sayable @saytag $ withContext wc sn
+      BUName sn (Just targs) -> withContext wc sn &+ withContext wc targs
+      BUOnOperator op -> sayable @saytag $ withContext wc op
+      BUOnOperatorT op targs -> withContext wc op &+ withContext wc targs -- ?
+      BUDestructorUnresolvedType urt -> '~' &+ withContext wc urt
+      BUDestructorSimpleId sn Nothing -> '~' &+ withContext wc sn
+      BUDestructorSimpleId sn (Just targs) -> '~' &+ withContext wc sn &+ withContext wc targs
+
+
+instance {-# OVERLAPPABLE #-}
+  $(sayableConstraints ''UnresolvedType
+   ) => Sayable saytag (WithContext UnresolvedType) where
+  sayable wc =
+    case contextData wc of
+      URTTemplate tp Nothing -> sayable @saytag $ withContext wc tp
+      URTTemplate tp (Just targs) -> withContext wc tp &+ withContext wc targs
+      URTDeclType dt -> sayable @saytag $ withContext wc dt
+      URTSubstPrefix pfx -> sayable @saytag $ withContext wc pfx
+
+
+instance {-# OVERLAPPABLE #-}
+  $(sayableConstraints ''UnresolvedQualifierLevel
+   ) => Sayable saytag (WithContext UnresolvedQualifierLevel) where
+  sayable wc =
+    case contextData wc of
+      URQL sn Nothing -> sayable @saytag $ withContext wc sn
+      URQL sn (Just targs) -> withContext wc sn &+ withContext wc targs
+
+
+instance {-# OVERLAPPABLE #-}
+  $(sayableConstraints ''DeclType
+   ) => Sayable saytag (WithContext DeclType) where
+  sayable wc =
+    case contextData wc of
+      DeclType expr -> t'"decltype (" &+ withContext wc expr &+ ')'
+      DeclTypeExpr expr -> t'"decltype (" &+ withContext wc expr &+ ')'
+
+
 instance {-# OVERLAPPABLE #-} Sayable saytag (WithContext ClosurePrefix) where
-  sayable (WC _p _c) = cannotSay Demangler "sayable"
-                       [ "No Sayable for ClosurePrefix" ]
+  sayable _ = cannotSay Demangler "sayable"
+              [ "No Sayable for ClosurePrefix" ]
 
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''Substitution
   ) => Sayable saytag (WithContext Substitution) where
-  sayable (WC p c) =
-    case p of
+  sayable wc =
+    case contextData wc of
       SubStd -> t'"std" &+ t'""
       SubAlloc -> t'"std" &+ t'"::allocator"
       SubBasicString -> t'"std" &+ t'"::basic_string"
-      SubStdType stdTy -> sayable @saytag $ WC stdTy c
+      SubStdType stdTy -> sayable @saytag $ withContext wc stdTy
 
 instance {-# OVERLAPPABLE #-} Sayable saytag (WithContext StdType) where
-  sayable (WC stdTy _c) =
+  sayable wc =
     let ct = t'"std::char_traits<char>" in
-    case stdTy of
+    case contextData wc of
       BasicStringChar -> t'"std::basic_string<char," &- ct &+ t'", std::allocator<char> >"
       BasicIStream -> t'"std::basic_istream<char," &- ct &- '>'
       BasicOStream -> t'"std::basic_ostream<char," &- ct &- '>'
@@ -445,55 +648,63 @@
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''ABI_Tag
   ) => Sayable saytag (WithContext ABI_Tag) where
-  sayable (WC (ABITag p) c) = t'"[abi:" &+ WC p c &+ ']'
+  sayable wc = let ABITag p = contextData wc in t'"[abi:" &+ withContext wc p &+ ']'
 
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''Type_
  ) => Sayable saytag (WithContext Type_) where
-  sayable (WC ty c) =
-    case ty of
-      BaseType t -> sayable @saytag $ WC t c
-      QualifiedType [] [] t -> sayable @saytag $ WC t c
-      QualifiedType eqs [] t -> WC t c &+ ctxLst' eqs c " "
-      QualifiedType [] cvqs t -> WC t c &- ctxLst' cvqs c " "
-      QualifiedType eqs cvqs t -> WC t c &- ctxLst' eqs c " " &- ctxLst' cvqs c " "
-      ClassUnionStructEnum n -> sayable @saytag $ WC n c
-      ClassStruct n -> sayable @saytag $ WC n c
-      Union n -> sayable @saytag $ WC n c
-      Enum n -> sayable @saytag $ WC n c
-      Function {} -> sayFunctionType ty "" c
-      Pointer f@(Function {}) -> sayFunctionType f "(*)" c
-      Pointer (ArrayType bnd t) -> WC t c &- t'"(*)" &- '[' &+ WC bnd c &+ ']'
-      Pointer t -> WC t c &+ '*'
-      LValRef (ArrayType bnd t) -> WC t c &- t'"(&)" &- '[' &+ WC bnd c &+ ']'
-      LValRef t -> WC t c &+ '&'
-      RValRef t -> WC t c &+ t'"&&"
-      ComplexPair t -> WC t c &- t'"complex"
-      Imaginary t -> WC t c &- t'"imaginary"
-      ArrayType bnd t -> WC t c &+ '[' &+ WC bnd c &+ ']'
-      Template tp ta -> WC tp c &- WC ta c -- ??
+  sayable wc =
+    case contextData wc of
+      BaseType t -> sayable @saytag $ withContext wc t
+      QualifiedType [] [] t -> sayable @saytag $ withContext wc t
+      QualifiedType eqs [] t -> withContext wc t &+ ctxLst' eqs wc " "
+      QualifiedType [] cvqs t -> withContext wc t &- ctxLst' cvqs wc " "
+      QualifiedType eqs cvqs t -> withContext wc t &- ctxLst' eqs wc " " &- ctxLst' cvqs wc " "
+      ClassUnionStructEnum n -> sayable @saytag $ withContext wc n
+      ClassStruct n -> sayable @saytag $ withContext wc n
+      Union n -> sayable @saytag $ withContext wc n
+      Enum n -> sayable @saytag $ withContext wc n
+      ty@(Function {}) -> sayFunctionType ty (t'"") wc
+      Pointer f@(Function {}) -> sayFunctionType f (t'"(*)") wc
+      Pointer (ArrayType bnd t) -> withContext wc t &- t'"(*)" &- '[' &+ withContext wc bnd &+ ']'
+      Pointer t -> withContext wc t &+ '*'
+      LValRef (ArrayType bnd t) -> withContext wc t &- t'"(&)" &- '[' &+ withContext wc bnd &+ ']'
+      LValRef t -> withContext wc t &+ '&'
+      RValRef (LValRef t@(QualifiedType _ [Const_] _)) ->
+        -- An rvalue may be used to initialize a const lvalue reference; see
+        -- https://en.cppreference.com/w/cpp/language/value_category
+        sayable @saytag $ withContext wc (LValRef t)
+      RValRef t -> withContext wc t &+ t'"&&"
+      ComplexPair t -> withContext wc t &- t'"complex"
+      Imaginary t -> withContext wc t &- t'"imaginary"
+      ArrayType bnd t -> withContext wc t &+ '[' &+ withContext wc bnd &+ ']'
+      Template tp ta -> withContext wc tp &- withContext wc ta -- ??
       Cpp11PackExpansion ts ->
         -- XXX expected some "..." (see
         -- https://en.cppreference.com/w/cpp/language/parameter-pack) but c++filt
         -- does not visibly decorate these.
-        ctxLst ts c
-      StdType stdTy -> sayable @saytag $ WC stdTy c
+        ctxLst ts wc
+      StdType stdTy -> sayable @saytag $ withContext wc stdTy
+      DeclType_ dt -> sayable @saytag $ withContext wc dt
+      PointerToMember cty mty@(Function {}) ->
+        sayFunctionType mty ('(' &+ withContext wc cty &+ t'"::*)") wc
+      PointerToMember cty mty -> '(' &+ withContext wc cty &+ t'"::*)" &+ withContext wc mty -- ??
 
 
-sayFunctionType :: Type_ -> Text -> Context -> Saying saytag
-sayFunctionType (Function cvqs mb'exc trns isExternC rTy argTys mb'ref) nm c =
-  ctxLst' cvqs c " "
-  &? wCtx mb'exc c
-  &+ WC trns c
+sayFunctionType :: Sayable saytag nm => Type_ -> nm -> WithContext Type_ -> Saying saytag
+sayFunctionType (Function cvqs mb'exc trns isExternC rTy argTys mb'ref) nm wc =
+  ctxLst' cvqs wc " "
+  &? wCtx mb'exc wc
+  &+ withContext wc trns
   &+ (if isExternC then t'" extern \"C\"" else t'"")
-  &+ WC rTy c
+  &+ withContext wc rTy
   &- nm &+ '('
   &+* (case argTys of
           BaseType Void :| [] -> []
-          _ -> wCtx (NEL.toList argTys) c
+          _ -> wCtx (NEL.toList argTys) wc
       )
   &+ ')'
-  &? wCtx mb'ref c
+  &? wCtx mb'ref wc
 sayFunctionType _ _ _ = cannotSay Demangler "sayFunctionType"
                         [ "Called with a type that is not a Function!" ]
 
@@ -501,48 +712,48 @@
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''ArrayBound
   ) => Sayable saytag (WithContext ArrayBound) where
-  sayable (WC n c) =
-    case n of
+  sayable wc =
+    case contextData wc of
       NoBounds -> sayable @saytag $ t'""
       NumBound i -> sayable @saytag i
-      ExprBound e -> sayable @saytag $ WC e c
+      ExprBound e -> sayable @saytag $ withContext wc e
 
 
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''ExceptionSpec
   ) => Sayable saytag (WithContext ExceptionSpec) where
-  sayable (WC exc c) =
-    case exc of
+  sayable wc =
+    case contextData wc of
       NonThrowing -> sayable @saytag $ t'"noexcept"
-      ComputedThrow expr -> t'"throw" &- WC expr c -- ?
-      Throwing tys -> t'"throw (" &+ wCtx tys c &+ ')' -- ?
+      ComputedThrow expr -> t'"throw" &- withContext wc expr -- ?
+      Throwing tys -> t'"throw (" &+ wCtx tys wc &+ ')' -- ?
 
 instance {-# OVERLAPPABLE #-} Sayable saytag (WithContext Transaction) where
-  sayable (WC trns _c) =
-    case trns of
+  sayable wc =
+    case contextData wc of
       TransactionSafe -> sayable @saytag $ t'"safe" -- ?
       TransactionUnsafe -> sayable @saytag $ t'""
 
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''BaseType
   ) => Sayable saytag (WithContext BaseType) where
-  sayable (WC t c) =
-    case lookup t builtinTypeTable of
+  sayable wc =
+    case lookup (contextData wc) builtinTypeTable of
       Just (_,s,_) -> sayable @saytag s
       Nothing ->
-        case t of
+        case contextData wc of
           FloatN n -> t'"std::float" &+ n &+ t'"_t"
           FloatNx n -> t'"std::float" &+ n &+ t'"x_t"
           SBitInt n -> t'"signed _BitInt(" &+ n &+ ')'
           UBitInt n -> t'"unsigned _BitInt(" &+ n &+ ')'
-          VendorExtendedType nm mb'ta -> WC nm c &? wCtx mb'ta c
-          _ -> cannotSay Demangler "sayable.Basetype"
+          VendorExtendedType nm mb'ta -> withContext wc nm &? wCtx mb'ta wc
+          t -> cannotSay Demangler "sayable.Basetype"
                [ "Unknown BaseType not listed in the builtinTypeTable"
                , show t
                ]
 
 instance {-# OVERLAPPABLE #-} Sayable saytag (WithContext CallOffset) where
-  sayable (WC _co _c) =
+  sayable _wc =
     cannotSay Demangler "Sayable CallOffset"
     [ "The CallOffset is for a thunk or covariant return thunk"
     , "and is not expected to be printed."
@@ -551,10 +762,11 @@
 instance {-# OVERLAPPABLE #-}
   $(sayableConstraints ''SubsCandidate
   ) => Sayable saytag (WithContext SubsCandidate) where
-  sayable (WC cand c) = -- For debug only
-    case cand of
-      SC_Type t -> t'"SC_Ty" &- WC t c
-      SC_UQName True n -> t'"SC_UN" &- t'"std::" &+ WC n c
-      SC_UQName _ n -> t'"SC_UN" &- WC n c
-      SC_Prefix p -> t'"SC_PR" &- WC p c
-      SC_TemplatePrefix tp -> t'"SC_TP" &- WC tp c
+  sayable wc = -- For debug only
+    case contextData wc of
+      SC_Type t -> t'"SC_Ty" &- withContext wc t
+      SC_UQName True n -> t'"SC_UN" &- t'"std::" &+ withContext wc n
+      SC_UQName _ n -> t'"SC_UN" &- withContext wc n
+      SC_Prefix p -> t'"SC_PR" &- withContext wc p
+      SC_TemplatePrefix tp -> t'"SC_TP" &- withContext wc tp
+      SC_UnresolvedType urt -> t'"SC_URT" &- withContext wc urt
diff --git a/src/Demangler/Structure.hs b/src/Demangler/Structure.hs
--- a/src/Demangler/Structure.hs
+++ b/src/Demangler/Structure.hs
@@ -10,6 +10,15 @@
 
 import Demangler.Context
 
+-- | The Demangled data structure holds the demangled name in data-oriented
+-- format.  This format encodes the various roles and portions of the mangled
+-- name in an AST-like structure that closely matches the mangled specification.
+-- Unfortunately, this is a relatively messy representation that is not easy to
+-- work with, and where things that might seem simple (e.g. the base function
+-- name) can be encoded in a number of different ways.  Therefore, the details of
+-- this structure are not exported and it should either be rendered to printable
+-- version via the 'sayable' package or inspected via accessor functions (like
+-- 'functionName').
 
 data Demangled = Original Coord
                | Encoded Encoding
@@ -39,12 +48,16 @@
   deriving (Eq, Show)
 
 data Name = NameNested NestedName
-          | UnscopedName Bool UnqualifiedName -- Bool is "std::" prefix
+          | UnscopedName UnscopedName
           | UnscopedTemplateName Name TemplateArgs
           | LocalName Encoding Name (Maybe Discriminator)
           | StringLitName Encoding (Maybe Discriminator)
   deriving (Eq, Show)
 
+data UnscopedName = UnScName Bool UnqualifiedName -- Bool is "std::" prefix
+                  | UnScSubst Substitution
+  deriving (Eq, Show)
+
 data NestedName = NestedName Prefix UnqualifiedName
                   [CVQualifier] (Maybe RefQualifier)
                 | NestedTemplateName TemplatePrefix TemplateArgs
@@ -52,8 +65,17 @@
   deriving (Eq, Show)
 
 type FunctionEntity = Coord
-type Discriminator = Coord
 
+-- The Discriminator is simply a non-negative number used to identify a
+-- particular instance of a name.  It is the rather unusual case for C++ that
+-- there may be several distinct functions that have the exact same demangled
+-- representation.  This can also happen if multiple entities with the same name
+-- in different scopes.  The Discriminator is used to identify which actual
+-- function is being referenced, even though it's not possible to tell which one
+-- based on the visible demangled name.
+newtype Discriminator = Discriminator Natural
+  deriving (Eq, Show)
+
 data ModuleName = ModuleName IsPartition SourceName
   deriving (Eq, Show)
 
@@ -64,7 +86,7 @@
                      | CtorDtorName CtorDtor
                      | StdSubst Substitution
                      | ModuleNamed [ModuleName] UnqualifiedName
-                      --  | UnnamedTypeName ...  starts with "U"
+                     | UnnamedTypeName (Maybe Natural) -- Nothing = first instance
                      --  | StructuredBinding ...
   deriving (Eq, Show)
 
@@ -185,6 +207,8 @@
            | Cpp11PackExpansion (NonEmpty Type_)
            | StdType StdType
            | ArrayType ArrayBound Type_
+           | DeclType_ DeclType
+           | PointerToMember Type_ Type_  -- class type, member type
   deriving (Eq, Show)
 
 data ArrayBound = NumBound Int
@@ -275,11 +299,55 @@
 
 type TemplateParam = TemplateArg
 
-data Expression = ExprPack Expression
+data Expression = ExprUnary Operator Expression
+                | ExprBinary Operator Expression Expression
+                | ExprTrinary Operator Expression Expression Expression
+                | ExprPfxPlus Expression
+                | ExprPfxMinus Expression
+                | ExprCall (NonEmpty Expression) -- call target :| [args]
+                | ExprConvert1 Type_ Expression
+                | ExprConvertSome Type_ (NonEmpty Expression)
+                | ExprConvertInit Type_ [BracedExpression]
+                | ExprBracedInit [BracedExpression]
+                | ExprNew GlobalScope [Expression] Type_
+                | ExprNewInit GlobalScope [Expression] Type_ InitializerExpr
+                | ExprNewArray GlobalScope [Expression] Type_
+                | ExprNewInitArray GlobalScope [Expression] Type_ InitializerExpr
+                | ExprDel GlobalScope Expression
+                | ExprDelArray GlobalScope Expression
+                | ExprDynamicCast Type_ Expression
+                | ExprStaticCast Type_ Expression
+                | ExprConstCast Type_ Expression
+                | ExprReinterpretCast Type_ Expression
+                | ExprTypeIdType Type_
+                | ExprTypeId Expression
+                | ExprSizeOfType Type_
+                | ExprSizeOf Expression
+                | ExprAlignOfType Type_
+                | ExprAlignOf Expression
+                | ExprNoException Expression
                 | ExprTemplateParam TemplateParam
+                | ExprFunctionParam FunctionParam
+                | ExprField Expression UnresolvedName
+                | ExprFieldPtr Expression UnresolvedName
+                | ExprFieldExpr Expression Expression
+                | ExprSizeOfTmplParamPack TemplateParam
+                | ExprSizeOfFuncParamPack FunctionParam
+                | ExprSizeOfCapturedTmplParamPack [TemplateArg]
+                | ExprPack Expression
+                | ExprUnaryLeftFold Operator Expression
+                | ExprUnaryRightFold Operator Expression
+                | ExprBinaryLeftFold Operator Expression Expression
+                | ExprBinaryRightFold Operator Expression Expression
+                | ExprThrow Expression
+                | ExprReThrow
+                | ExprVendorExtended SourceName [TemplateArg]
+                | ExprUnresolvedName UnresolvedName
                 | ExprPrim ExprPrimary
   deriving (Eq, Show)
 
+type GlobalScope = Bool
+
 data ExprPrimary = IntLit Type_ Int
                  | FloatLit Type_ Float
                  | DirectLit Type_  -- String or NullPtr
@@ -288,111 +356,160 @@
                  | ExternalNameLit Encoding
   deriving (Eq, Show)
 
+data BracedExpression = BracedExpr Expression
+                      | BracedFieldExpr SourceName BracedExpression -- .name = expr
+                      | BracedIndexExpr Expression BracedExpression -- [idx] = expr
+                      | BracedRangedExpr Expression Expression BracedExpression
+                        -- [expr ... expr] = expr
+  deriving (Eq, Show)
+
+data InitializerExpr = Initializer [Expression]
+  deriving (Eq, Show)
+
+data FunctionParam = FP_This
+                   | FP_ [CVQualifier] Natural
+  deriving (Eq, Show)
+
+data UnresolvedName = URN_Base GlobalScope BaseUnresolvedName
+                    | URNScopedRef UnresolvedType BaseUnresolvedName
+                    | URNSubScopedRef UnresolvedType
+                      UnresolvedQualifierLevels BaseUnresolvedName
+                    | URNQualRef GlobalScope
+                      UnresolvedQualifierLevels BaseUnresolvedName
+  deriving (Eq, Show)
+
+type UnresolvedQualifierLevels = NonEmpty UnresolvedQualifierLevel
+
+data BaseUnresolvedName = BUName SourceName (Maybe TemplateArgs)
+                        | BUOnOperatorT Operator TemplateArgs
+                        | BUOnOperator Operator
+                        | BUDestructorUnresolvedType UnresolvedType
+                        | BUDestructorSimpleId SourceName (Maybe TemplateArgs)
+  deriving (Eq, Show)
+
+
+data UnresolvedType = URTTemplate TemplateParam (Maybe TemplateArgs)
+                    | URTDeclType DeclType
+                    | URTSubstPrefix Prefix -- never parsed: subst insertion
+  deriving (Eq, Show)
+
+
+data UnresolvedQualifierLevel = URQL SourceName (Maybe TemplateArgs)
+  deriving (Eq, Show)
+
 type ClosurePrefix = () -- XXX TBD
-type DeclType = () -- XXX TBD
 
+
+data DeclType = DeclType Expression
+              | DeclTypeExpr Expression
+  deriving (Eq, Show)
+
+
 -- | Table of builtin types as the internal BaseType representation, followed by
 -- a tuple of strings.  The first string is the reference to this type in a
 -- mangled name.  The second string is the C/C++ type name to be used when
--- writing a value cast.  The third string, if non-empty, is a C/C++ suffix that
+-- writing a value cast.  The third string, if specified, is a C/C++ suffix that
 -- can be written after literal values to indicate the type instead (for example,
 -- emit `10ul` instead of `(unsigned long)10`).
 
-builtinTypeTable :: [ (BaseType, (Text, Text, Text)) ]
+builtinTypeTable :: [ (BaseType, (Text, Text, Maybe Text)) ]
 builtinTypeTable =
-  [ (Void, ("v", "void", ""))
-  , (Wchar_t, ("w", "wchar_t", ""))
-  , (Bool_, ("b", "bool", ""))
-  , (Char_, ("c", "char", ""))
-  , (SChar, ("a", "signed char", ""))
-  , (UChar, ("h", "unsigned char", ""))
-  , (Short, ("s", "short", ""))
-  , (UShort, ("t", "unsigned short", ""))
-  , (Int_, ("i", "int", ""))
-  , (UInt, ("j", "unsigned int", ""))
-  , (Long, ("l", "long", "l"))
-  , (ULong, ("m", "unsigned long", "ul"))
-  , (LongLong, ("x", "long long", "")) -- __int64
-  , (ULongLong, ("y", "unsigned long long", "")) -- __int64
-  , (Int128, ("n", "__int128", ""))
-  , (UInt128, ("o", "unsigned __int128", ""))
-  , (Float_, ("f", "float", ""))
-  , (Double_, ("d", "double", ""))
-  , (LongDouble80, ("e", "long double", "")) -- __float80
-  , (Float128, ("g", "__float128", ""))
-  , (Ellipsis, ("z", "...", ""))
-  , (IEE754rDecFloat64, ("Dd", "__ieeefloat64", "")) -- ??
-  , (IEE754rDecFloat128, ("De", "__ieeefloat128", "")) -- ??
-  , (IEE754rDecFloat32, ("Df", "__ieeefloat32", "")) -- ??
-  , (IEE754rDecFloat16, ("Dh", "__ieeefloat16", "")) -- ??
-  , (BFloat16, ("DF16b", "std::bfloat16_t", ""))
-  , (Char32, ("Di", "char32_t", ""))
-  , (Char16, ("Ds", "char16_t", ""))
-  , (Char8, ("Du", "char8_t", ""))
-  , (Auto, ("Da", "auto", ""))
-  , (DeclTypeAuto, ("Dc", "decltype(auto)", "")) -- ??
-  , (NullPtr, ("Dn", "std::nullptr_t", "")) -- decltype(nullptr)
-  , (N1168FixedPointAccum, ("DA", "T _Accum", "")) -- ??
-  , (N1168FixedPointAccumSat, ("DS DA", "_Sat T _Accum", "")) -- ??
-  , (N1168FixedPointFract, ("DR", "T _Fract", "")) -- ??
-  , (N1168FixedPointFractSat, ("DS DR", "_Sat T _Fract", "")) -- ??
+  [ (Void, ("v", "void", Nothing))
+  , (Wchar_t, ("w", "wchar_t", Nothing))
+  , (Bool_, ("b", "bool", Nothing))
+  , (Char_, ("c", "char", Nothing))
+  , (SChar, ("a", "signed char", Nothing))
+  , (UChar, ("h", "unsigned char", Nothing))
+  , (Short, ("s", "short", Nothing))
+  , (UShort, ("t", "unsigned short", Nothing))
+  , (Int_, ("i", "int", Just ""))
+  , (UInt, ("j", "unsigned int", Just "u"))
+  , (Long, ("l", "long", Just "l"))
+  , (ULong, ("m", "unsigned long", Just "ul"))
+  , (LongLong, ("x", "long long", Nothing)) -- __int64
+  , (ULongLong, ("y", "unsigned long long", Nothing)) -- __int64
+  , (Int128, ("n", "__int128", Nothing))
+  , (UInt128, ("o", "unsigned __int128", Nothing))
+  , (Float_, ("f", "float", Nothing))
+  , (Double_, ("d", "double", Nothing))
+  , (LongDouble80, ("e", "long double", Nothing)) -- __float80
+  , (Float128, ("g", "__float128", Nothing))
+  , (Ellipsis, ("z", "...", Nothing))
+  , (IEE754rDecFloat64, ("Dd", "__ieeefloat64", Nothing)) -- ??
+  , (IEE754rDecFloat128, ("De", "__ieeefloat128", Nothing)) -- ??
+  , (IEE754rDecFloat32, ("Df", "__ieeefloat32", Nothing)) -- ??
+  , (IEE754rDecFloat16, ("Dh", "__ieeefloat16", Nothing)) -- ??
+  , (BFloat16, ("DF16b", "std::bfloat16_t", Nothing))
+  , (Char32, ("Di", "char32_t", Nothing))
+  , (Char16, ("Ds", "char16_t", Nothing))
+  , (Char8, ("Du", "char8_t", Nothing))
+  , (Auto, ("Da", "auto", Nothing))
+  , (DeclTypeAuto, ("Dc", "decltype(auto)", Nothing)) -- ??
+  , (NullPtr, ("Dn", "std::nullptr_t", Nothing)) -- decltype(nullptr)
+  , (N1168FixedPointAccum, ("DA", "T _Accum", Nothing)) -- ??
+  , (N1168FixedPointAccumSat, ("DS DA", "_Sat T _Accum", Nothing)) -- ??
+  , (N1168FixedPointFract, ("DR", "T _Fract", Nothing)) -- ??
+  , (N1168FixedPointFractSat, ("DS DR", "_Sat T _Fract", Nothing)) -- ??
   ]
 
-opTable :: [ (Operator, (Text, Text)) ]
+data Arity = Unary | Binary | Trinary | NoArity
+  deriving Eq
+
+opTable :: [ (Operator, (Arity, (Text, Text))) ]
 opTable =
-  [ (OpNew,         ("nw", " new"))
-  , (OpNewArr,      ("na", " new[]"))
-  , (OpDel,         ("dl", " delete"))
-  , (OpDelArr,      ("da", " delete[]"))
-  , (OpCoAwait,     ("aw", " co_await"))
-  , (OpPositive,    ("ps", "+"))
-  , (OpNegative,    ("ng", "-"))
-  , (OpAddress,     ("ad", "&"))
-  , (OpDeReference, ("de", "*"))
-  , (OpComplement,  ("co", "~"))
-  , (OpPlus,        ("pl", "+"))
-  , (OpMinus,       ("mi", "-"))
-  , (OpMultiply,    ("ml", "*"))
-  , (OpDivide,      ("dv", "/"))
-  , (OpRemainder,   ("rm", "%"))
-  , (OpAnd,         ("an", "&"))
-  , (OpOr,          ("or", "|"))
-  , (OpXOR,         ("eo", "^"))
-  , (OpAssign,      ("aS", "="))
-  , (OpAssignPlus,  ("pL", "+="))
-  , (OpAssignMinus, ("mI", "-="))
-  , (OpAssignMul,   ("mL", "*="))
-  , (OpAssignDiv,   ("dV", "/="))
-  , (OpAssignRem,   ("rM", "%="))
-  , (OpAssignAnd,   ("aN", "&="))
-  , (OpAssignOr,    ("oR", "|="))
-  , (OpAssignXOR,   ("eO", "^="))
-  , (OpLeftShift,   ("ls", "<<"))
-  , (OpRightShift,  ("rs", ">>"))
-  , (OpAssignShL,   ("lS", "<<="))
-  , (OpAssignShR,   ("rS", ">>="))
-  , (OpEq,          ("eq", "=="))
-  , (OpNotEq,       ("ne", "!="))
-  , (OpLT,          ("lt", "<"))
-  , (OpGT,          ("gt", ">"))
-  , (OpLTE,         ("le", "<="))
-  , (OpGTE,         ("ge", ">="))
-  , (OpSpan,        ("ss", "<=>"))
-  , (OpNot,         ("nt", "!"))
-  , (OpLogicalAnd,  ("aa", "&&"))
-  , (OpLogicalOr,   ("oo", "||"))
-  , (OpPlusPlus,    ("pp", "++"))
-  , (OpMinusMinus,  ("mm", "--"))
-  , (OpComma,       ("cm", ","))
-  , (OpPointStar,   ("pm", "->*"))
-  , (OpPoint,       ("pt", "->"))
-  , (OpCall,        ("cl", "()"))
-  , (OpIndex,       ("ix", "[]"))
-  , (OpQuestion,    ("qu", "?"))
-  , (OpSizeOfType,  ("st", " sizeof"))
-  , (OpAlignOfType, ("at", " alignof"))
-  , (OpSizeOfExpr,  ("sz", " sizeof"))
-  , (OpAlignOfExpr, ("az", " alignof"))
+  [ (OpNew,         (NoArity, ("nw", " new")))
+  , (OpNewArr,      (NoArity, ("na", " new[]")))
+  , (OpDel,         (NoArity, ("dl", " delete")))
+  , (OpDelArr,      (NoArity, ("da", " delete[]")))
+  , (OpCoAwait,     (NoArity, ("aw", " co_await")))
+  , (OpPositive,    (Unary, ("ps", "+")))
+  , (OpNegative,    (Unary, ("ng", "-")))
+  , (OpAddress,     (Unary, ("ad", "&")))
+  , (OpDeReference, (Unary, ("de", "*")))
+  , (OpComplement,  (Unary, ("co", "~")))
+  , (OpPlus,        (Binary, ("pl", "+")))
+  , (OpMinus,       (Binary, ("mi", "-")))
+  , (OpMultiply,    (Binary, ("ml", "*")))
+  , (OpDivide,      (Binary, ("dv", "/")))
+  , (OpRemainder,   (Binary, ("rm", "%")))
+  , (OpAnd,         (Binary, ("an", "&")))
+  , (OpOr,          (Binary, ("or", "|")))
+  , (OpXOR,         (Binary, ("eo", "^")))
+  , (OpAssign,      (Binary, ("aS", "=")))
+  , (OpAssignPlus,  (Binary, ("pL", "+=")))
+  , (OpAssignMinus, (Binary, ("mI", "-=")))
+  , (OpAssignMul,   (Binary, ("mL", "*=")))
+  , (OpAssignDiv,   (Binary, ("dV", "/=")))
+  , (OpAssignRem,   (Binary, ("rM", "%=")))
+  , (OpAssignAnd,   (Binary, ("aN", "&=")))
+  , (OpAssignOr,    (Binary, ("oR", "|=")))
+  , (OpAssignXOR,   (Binary, ("eO", "^=")))
+  , (OpLeftShift,   (Binary, ("ls", "<<")))
+  , (OpRightShift,  (Binary, ("rs", ">>")))
+  , (OpAssignShL,   (Binary, ("lS", "<<=")))
+  , (OpAssignShR,   (Binary, ("rS", ">>=")))
+  , (OpEq,          (Binary, ("eq", "==")))
+  , (OpNotEq,       (Binary, ("ne", "!=")))
+  , (OpLT,          (Binary, ("lt", "<")))
+  , (OpGT,          (Binary, ("gt", ">")))
+  , (OpLTE,         (Binary, ("le", "<=")))
+  , (OpGTE,         (Binary, ("ge", ">=")))
+  , (OpSpan,        (Binary, ("ss", "<=>")))
+  , (OpNot,         (Unary, ("nt", "!")))
+  , (OpLogicalAnd,  (Binary, ("aa", "&&")))
+  , (OpLogicalOr,   (Binary, ("oo", "||")))
+  , (OpPlusPlus,    (Unary, ("pp", "++")))
+  , (OpMinusMinus,  (Unary, ("mm", "--")))
+  , (OpComma,       (Binary, ("cm", ",")))
+  , (OpPointStar,   (Binary, ("pm", "->*")))
+  , (OpPoint,       (Binary, ("pt", "->")))
+  , (OpCall,        (NoArity, ("cl", "()")))
+  , (OpIndex,       (NoArity, ("ix", "[]")))
+  , (OpQuestion,    (Trinary, ("qu", "?")))
+  , (OpSizeOfType,  (Unary, ("st", " sizeof")))
+  , (OpAlignOfType, (Unary, ("at", " alignof")))
+  , (OpSizeOfExpr,  (Unary, ("sz", " sizeof")))
+  , (OpAlignOfExpr, (Unary, ("az", " alignof")))
   ]
 
 
@@ -405,4 +522,5 @@
                      -- ^ Bool is True for std:: namespace prefix
                    | SC_Prefix Prefix
                    | SC_TemplatePrefix TemplatePrefix
+                   | SC_UnresolvedType UnresolvedType
   deriving (Eq, Show)
diff --git a/src/Demangler/Substitution.hs b/src/Demangler/Substitution.hs
--- a/src/Demangler/Substitution.hs
+++ b/src/Demangler/Substitution.hs
@@ -10,13 +10,14 @@
     -- * Parse a substitution reference
     substitution
     -- * Substitute the found substitution into the result
-  , substituteUnqualifiedName
+  , substituteUnscopedName
   , substitutePrefix
   , substitutePrefixR
   , substituteTemplateParam
   , substituteTemplatePrefix
   , substituteType
   , stdSubstToType
+  , substituteUnresolvedType
   -- * When a subtitution candidate has been parsed, it is recorded here
   , canSubstUnscopedTemplateName
   , canSubstPrefix
@@ -25,6 +26,7 @@
   , canSubstTemplatePrefix
   , canSubstType
   , canSubstTypes
+  , canSubstUnresolvedType
   , dropLastSubst
   )
 where
@@ -38,13 +40,13 @@
 import           Data.Sequence ( (|>), ViewR((:>)) )
 import qualified Data.Sequence as Seq
 
-import           Demangler.Context
 import           Demangler.Engine
 import           Demangler.Structure
 import           Demangler.PPrint ()
 
 #ifdef MIN_VERSION_panic
 -- The debug flag is enabled in the cabal file
+import           Demangler.Context
 import           Text.Sayable
 import           Debug.Trace
 #endif
@@ -126,11 +128,11 @@
 dumpSubs spec what = do
     mapM_ (traceM . show) $ Seq.zip
              (Seq.fromList [0.. Seq.length (spec ^. nSubs)])
-             ((\ue -> (ue, sez @"debug" (WC ue (spec ^. nContext))))
+             ((\ue -> (ue, sez @"debug" (addContext ue (spec ^. nContext))))
               <$> spec ^. nSubs)
     mapM_ (traceM . show) $ Seq.zip
              (Seq.fromList [0.. Seq.length (spec ^. nTmplSubs)])
-             ((\ue -> ('T', ue, sez @"debug" (WC ue (spec ^. nContext))))
+             ((\ue -> ('T', ue, sez @"debug" (addContext ue (spec ^. nContext))))
               <$> spec ^. nTmplSubs)
     traceM $ "Subs Total: " <> show ((Seq.length $ spec ^. nSubs) + (Seq.length $ spec ^. nTmplSubs)) <> " "
         <> show ((Seq.length $ spec ^. nSubs), (Seq.length $ spec ^. nTmplSubs))
@@ -159,11 +161,29 @@
          , show (spec ^. nVal)
          ]
 
-substituteUnqualifiedName :: Next Substitution UnqualifiedName
-                          -> Next Substitution' UnqualifiedName
-substituteUnqualifiedName direct i =
+substituteUnscopedName :: Next Substitution UnscopedName
+                          -> Next Substitution' UnscopedName
+substituteUnscopedName direct i =
   case getSubst i of
-    Right (Just (SC_UQName _ n)) -> ret i n
+    Right (Just (SC_UQName s n)) -> ret i $ UnScName s n
+    Right (Just (SC_Prefix p)) ->
+      let getUsn = \case
+            Prefix prefixr -> getUsn_PR prefixr
+            _ -> Nothing
+          getUsn_PR = \case
+            PrefixUQName uqn PrefixEnd -> Just $ UnScName False uqn
+            PrefixUQName (StdSubst SubStd) sp ->
+              case getUsn_PR sp of
+                Nothing -> Nothing
+                Just (UnScName _ uqn) -> Just $ UnScName True uqn
+                Just n@(UnScSubst _) -> Just n
+            PrefixUQName (StdSubst _) _ -> Nothing
+            PrefixUQName _ _ -> Nothing
+            PrefixEnd -> Nothing
+            PrefixTemplateArgs _ _ -> Nothing
+      in case getUsn p of
+           Nothing -> Nothing
+           Just usn -> ret i usn
     Right _ -> Nothing
     Left s -> direct =<< ret i s
 
@@ -173,7 +193,7 @@
     Right (Just (SC_Type t)) -> ret i t
     Right (Just (SC_Prefix p)) -> ret i =<< prefixToType p
     Right (Just (SC_UQName isStd uqn)) ->
-      ret i $ ClassUnionStructEnum $ UnscopedName isStd uqn
+      ret i $ ClassUnionStructEnum $ UnscopedName $ UnScName isStd uqn
     Right o -> invalidSubst "Type" i o
     Left s -> embed =<< ret i s
 
@@ -198,23 +218,51 @@
             mkntn tpfx = NestedTemplateName tpfx ta [] Nothing
         in NameNested . mkntn <$> tmpltpfx
 
+substituteUnresolvedType :: Next Substitution UnresolvedType
+                         -> Next Substitution' UnresolvedType
+substituteUnresolvedType direct i =
+  case getSubst i of
+    Right (Just (SC_Prefix p)) -> ret i $ URTSubstPrefix p
+    Right (Just (SC_UnresolvedType urt)) -> ret i urt
+    Right x -> cannot Demangler "substituteUnresolvedType"
+               [ "Cannot convert to an unresolved type: " <> show x ]
+    Left s -> direct =<< ret i s
 
-substitutePrefix :: (Next Substitution Prefix) -> Next Substitution' Prefix
+substitutePrefix :: Next Substitution Prefix -> Next Substitution' Prefix
 substitutePrefix direct i =
   case getSubst i of
     Right (Just (SC_Prefix p)) -> ret i p
     Right o -> invalidSubst "Prefix" i o
     Left s -> direct =<< ret i s
 
-substitutePrefixR :: (Next Substitution PrefixR) -> Next Substitution' PrefixR
+substitutePrefixR :: Next Substitution PrefixR -> Next Substitution' PrefixR
 substitutePrefixR direct i =
   case getSubst i of
-    Right (Just (SC_Type (ClassUnionStructEnum (UnscopedName False uqn)))) ->
-      ret i $ PrefixUQName uqn PrefixEnd
+    Right o@(Just (SC_Type (ClassUnionStructEnum nm))) ->
+      case name2prefix nm of
+        Just pfx -> ret i pfx
+        Nothing -> invalidSubst "PrefixR.SC_Type.ClassUnionStructEnum" i o
     Right (Just (SC_Prefix (Prefix sp))) -> ret i sp
     Right o -> invalidSubst "PrefixR" i o
     Left s -> ret i s >>= direct
+  where
+    name2prefix = \case
+      NameNested nn -> nn2prefix nn
+      UnscopedName (UnScName True _uqn) -> Nothing
+        -- Just $ PrefixUQName (SourceName (!!! "std") mempty) $ PrefixUQName uqn PrefixEnd
+      UnscopedName (UnScName False uqn) -> Just $ PrefixUQName uqn PrefixEnd
+      UnscopedName (UnScSubst s) -> Just $ PrefixUQName (StdSubst s) PrefixEnd
+      UnscopedTemplateName nm _tmplArgs -> name2prefix nm
+      LocalName _enc nm _disc -> name2prefix nm -- discriminators are invisible
+      StringLitName _ _ -> Nothing
+    nn2prefix = \case
+      NestedName pf@(Prefix _) uqn _cvq _mbref ->
+        case extendPrefix pf $ PrefixUQName uqn PrefixEnd of
+          Prefix pfr -> Just pfr
+          _ -> Nothing
+      _ -> Nothing
 
+
 substituteTemplatePrefix :: (Next Substitution TemplatePrefix)
                          -> Next Substitution' TemplatePrefix
 substituteTemplatePrefix direct i =
@@ -283,7 +331,7 @@
 canSubstUnscopedTemplateName :: Next Name Name
 canSubstUnscopedTemplateName i =
   case i ^. nVal of
-    UnscopedName isStd uqn -> canSubst (SC_UQName isStd uqn) i
+    UnscopedName (UnScName isStd uqn) -> canSubst (SC_UQName isStd uqn) i
     _ -> pure i
 
 
@@ -304,6 +352,9 @@
 canSubstTypes i =
   let subT i' ty = canSubst (SC_Type $ ty) i'
   in foldM subT i (i ^. nVal)
+
+canSubstUnresolvedType :: Next UnresolvedType UnresolvedType
+canSubstUnresolvedType i = canSubst (SC_UnresolvedType $ i ^. nVal) i
 
 canSubstTemplatePrefix :: Next TemplatePrefix TemplatePrefix
 canSubstTemplatePrefix i = canSubst (SC_TemplatePrefix $ i ^. nVal) i
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -5,7 +5,9 @@
 
 module Main (main) where
 
+import           Control.Monad ( unless )
 import           Control.Monad.Trans.State
+import qualified Data.List.NonEmpty as NEL
 import           Data.Text ( Text )
 import qualified Data.Text as T
 import qualified Data.Text.IO as TIO
@@ -17,7 +19,7 @@
 import           Test.Tasty.Hspec
 import           Text.Sayable
 
-import           Demangler ( demangle, demangle1, newDemangling )
+import           Demangler ( demangle, demangle1, newDemangling, functionName )
 
 -- n.b. Original conception was to use a Sandwich Context to retrieve the
 -- contents of the input data files, but a Context can only be retrieved (via
@@ -32,35 +34,47 @@
           defaultMain =<< (testSpec "Demangle" $ demangleTests inps)
 
 
-getInputData :: String -> IO (String, [(Text, Text)])
+getInputData :: String -> IO (String, [(Text, Text, [Text])])
 getInputData fname = do
   inputs <- TIO.readFile fname
   expects <- T.pack <$> readProcess "c++filt" [] (T.unpack inputs)
-  let pairs = zip (T.lines inputs) (T.lines expects)
-  return (fname, pairs)
+  funcNames <- fmap (T.splitOn (T.pack "`"))
+               . T.lines
+               <$> TIO.readFile (fname <> "-funcnames")
+  let matches = zip3 (T.lines inputs) (T.lines expects) (funcNames <> repeat [])
+  return (fname, matches)
 
 
-demangleTests :: [(String, [(Text, Text)])] -> Spec
+demangleTests :: [(String, [(Text, Text, [Text])])] -> Spec
 demangleTests testInputs = describe "Demangle tests" $
                            mapM_ demangleTestSet testInputs
 
-demangleTestSet :: (String, [(Text, Text)]) -> Spec
+demangleTestSet :: (String, [(Text, Text, [Text])]) -> Spec
 demangleTestSet (inpFile, inpData) =
   describe inpFile $
-  do describe "Individually" $ sequence_ (mkTest <$> inpData)
+  do describe "Solo" $ sequence_ (mkTest <$> inpData)
      describe "Batched" $ batchTest inpData
 
 
-mkTest :: (Text, Text) -> Spec
-mkTest (sym, expected) = it ("demangles " <> T.unpack sym) $
-  (toText $ demangle1 sym) `shouldBe` expected
+mkTest :: (Text, Text, [Text]) -> Spec
+mkTest (sym, expected, funcNames) = describe (T.unpack sym) $ do
+  let dm = demangle1 sym
+  it "demangles" $ (toText $ dm) `shouldBe` expected
+  unless (null funcNames || funcNames == [T.pack ""]) $
+    it "gets functionName" $
+    (NEL.toList <$> functionName dm) `shouldBe` Just funcNames
 
-batchTest :: [(Text, Text)] -> Spec
+batchTest :: [(Text, Text, [Text])] -> Spec
 batchTest symbols =
-  let r = runState (sequence (state . demangle . fst <$> symbols)) newDemangling
-      mkRes (dm, (inp, ex)) prs = (inp, toText (dm, snd r), ex) : prs
+  let r = runState (sequence (state . demangle <$> syms)) newDemangling
+      syms = (\(a,_,_) -> a) <$> symbols
       results = foldr mkRes [] $ zip (fst r) symbols
-      validate (i,rslt,e) = it ("demangles " <> T.unpack i) $ rslt `shouldBe` e
+      mkRes (dm, (inp, ex, fne)) prs =
+        (inp, toText (dm, snd r), ex, functionName (dm, snd r), fne) : prs
+      validate (i,rslt,e,fn,fne) = do
+        it ("demangles " <> T.unpack i) $ rslt `shouldBe` e
+        unless (null fne || fne == [T.pack ""]) $
+          it ("functionName " <> T.unpack i) $ (NEL.toList <$> fn) `shouldBe` Just fne
   in sequence_ (validate <$> results)
 
 toText :: Sayable "normal" a => a -> Text
diff --git a/test/full-test-cases.txt-funcnames b/test/full-test-cases.txt-funcnames
new file mode 100644
--- /dev/null
+++ b/test/full-test-cases.txt-funcnames
diff --git a/test/initial-test-cases.txt b/test/initial-test-cases.txt
--- a/test/initial-test-cases.txt
+++ b/test/initial-test-cases.txt
@@ -47,3 +47,18 @@
 _ZN17PositionSmoothing18_generateSetpointsERKN6matrix7Vector3IfEERA3_S3_bS4_fbRNS_26PositionSmoothingSetpointsE
 _ZN17PositionSmoothing18_generateSetpointsERKN6matrix7Vector3IfEERA_S3_bS4_fbRNS_26PositionSmoothingSetpointsE
 _ZZ23airship_att_recalibratevE4amsg
+_ZNSt6vectorIN5boost10shared_ptrIN3ros12TimerManagerINS2_10SteadyTimeENS2_12WallDurationENS2_16SteadyTimerEventEE9TimerInfoEEESaIS9_EE17_M_realloc_insertIJRKS9_EEEvN9__gnu_cxx17__normal_iteratorIPS9_SB_EEDpOT_
+_ZN5boost9function4IvRKNS_10shared_ptrIN3ros10ConnectionEEERKNS_12shared_arrayIhEEjbEC2INS_3_bi6bind_tIvNS_4_mfi3mf4IvNS2_22TransportPublisherLinkES6_SA_jbEENSD_5list5INSD_5valueIPSH_EENS_3argILi1EEENSN_ILi2EEENSN_ILi3EEENSN_ILi4EEEEEEEEET_NS_10enable_if_IXntsrNS_11is_integralISU_EE5valueEiE4typeE
+_Z3fooIXtl7MyArrayLi1ELi2ELi3EEEEPKci
+_Z3barIXcv7MyArrayilLi1ELi2ELi3EEEEPKci
+_Z3bazIXtl1Xdi1adi1bdxLi3ELi1EEEEPKci
+_ZZN15AP_MotorsMatrix21setup_octaquad_matrixEN9AP_Motors16motor_frame_typeEE6motors_5
+_ZN3FooUt3_C2Ev
+_ZN9AP_Logger4initERK9AP_ParamTIiL11ap_var_type3EEPK12LogStructureh
+_ZL3MINIf9AP_ParamTIfL11ap_var_type4EEEDTqultfp_fp0_fp_fp0_ERKT_RKT0_
+_ZN7FunctorIvJhbjEE4bindIN6AP_HAL9PWMSourceEXadL_ZNS3_11irq_handlerEhbjEEEES0_PT_
+_ZN7FunctorIvJEE14method_wrapperIN13AP_Networking4PortEXadL_ZNS3_15tcp_client_loopEvEEEEvPv
+_Z2sqIdJddEEdT_DpKT0_
+_ZN6Canard11ObjCallbackI11AP_DroneCAN26com_hobbywing_esc_GetEscIDEC2EPS1_MS1_FvRK16CanardRxTransferRKS2_E
+_ZNSt7complexIfEmIIfEERS0_RKS_IT_E
+_ZNVK20AP_ESC_Telem_Backend13TelemetryData5staleEj
diff --git a/test/initial-test-cases.txt-funcnames b/test/initial-test-cases.txt-funcnames
new file mode 100644
--- /dev/null
+++ b/test/initial-test-cases.txt-funcnames
@@ -0,0 +1,64 @@
+qAllocMore
+qHBNewFace
+foobar
+qAllocMore
+qAllocMore
+qAllocMore
+qAllocMore
+qAllocMore
+read_gyro
+uorb_unsubscribe`uorb
+
+__const._Z23airship_att_recalibratev.amsg
+operator|
+operator&
+operator&
+qt_qFindChildren_helper
+qUnregisterResourceData
+qstricmp
+foo`foo
+vehicle_gps_stop
+vehicle_gps_stop
+vehicle_gps_start
+vehicle_gps_town
+vehicle_gps_town
+hrt_tim_isr
+publish`Publication`uORB
+publish`Publication`uORB
+Quaternion`Quaternion`matrix
+operator[]`map`std
+
+
+operator+`std
+fill_n`std
+__copy_move_backward_a2`std
+forward_as_tuple`std
+forward`std
+__ucr`__uninitialized_construct_buf_dispatch`std
+operator<<`larcfm
+qqq`kwq
+
+drawItemText`QGtkStyle
+~QTextEdit`QTextEdit
+operator new
+WorkQueueRunner`px4
+get_socket_path`px4_daemon
+get_socket_path`px4_daemon
+_generateSetpoints`PositionSmoothing
+_generateSetpoints`PositionSmoothing
+
+_M_realloc_insert`vector`std
+function4`function4`boost
+foo
+bar
+baz
+setup_octaquad_matrix`AP_MotorsMatrix
+Foo`unnamed_type_num5`Foo
+init`AP_Logger
+
+bind`Functor
+method_wrapper`Functor
+sq
+ObjCallback`ObjCallback`Canard
+operator-=`complex`std
+stale`TelemetryData`AP_ESC_Telem_Backend
