[MS Demangler] Add rudimentary C++11 Support
authorZachary Turner <zturner@google.com>
Mon, 30 Jul 2018 23:02:10 +0000 (23:02 +0000)
committerZachary Turner <zturner@google.com>
Mon, 30 Jul 2018 23:02:10 +0000 (23:02 +0000)
This patch adds support for demangling r-value references, new
operators such as the ""_foo operator, lambdas, alias types,
nullptr_t, and various other C++11'isms.

There is 1 failing test remaining in this file, which appears to
be related to back-referencing. This type of problem has the
potential to get ugly so I'd rather fix it in a separate patch.

Differential Revision: https://reviews.llvm.org/D50013

llvm-svn: 338324

llvm/lib/Demangle/MicrosoftDemangle.cpp
llvm/test/Demangle/ms-cxx11.test [new file with mode: 0644]

index f7c7864..b926ffa 100644 (file)
@@ -139,7 +139,7 @@ enum class StorageClass : uint8_t {
 
 enum class QualifierMangleMode { Drop, Mangle, Result };
 
-enum class PointerAffinity { Pointer, Reference };
+enum class PointerAffinity { Pointer, Reference, RValueReference };
 
 // Calling conventions
 enum class CallingConv : uint8_t {
@@ -163,7 +163,6 @@ enum class PrimTy : uint8_t {
   None,
   Function,
   Ptr,
-  Ref,
   MemberPtr,
   Array,
 
@@ -177,6 +176,8 @@ enum class PrimTy : uint8_t {
   Char,
   Schar,
   Uchar,
+  Char16,
+  Char32,
   Short,
   Ushort,
   Int,
@@ -189,6 +190,7 @@ enum class PrimTy : uint8_t {
   Float,
   Double,
   Ldouble,
+  Nullptr
 };
 
 // Function classes
@@ -205,14 +207,20 @@ enum FuncClass : uint8_t {
 namespace {
 
 struct Type;
+struct Name;
 
 // Represents a list of parameters (template params or function arguments.
 // It's represented as a linked list.
 struct ParamList {
   bool IsVariadic = false;
 
+  // If this is a type, Current will be valid and AliasName will be null.
   Type *Current = nullptr;
 
+  // If this is an alias (e.g. using X = Y), Current will be null and AliasName
+  // will be valid.
+  Name *AliasName = nullptr;
+
   ParamList *Next = nullptr;
 };
 
@@ -254,7 +262,7 @@ struct Name {
   StringView Operator;
 
   // Template parameters. Null if not a template.
-  ParamList TemplateParams;
+  ParamList *TemplateParams = nullptr;
 
   // Nested BackReferences (e.g. "A::B::C") are represented as a linked list.
   Name *Next = nullptr;
@@ -265,6 +273,8 @@ struct PointerType : public Type {
   void outputPre(OutputStream &OS) override;
   void outputPost(OutputStream &OS) override;
 
+  PointerAffinity Affinity;
+
   // Represents a type X in "a pointer to X", "a reference to X",
   // "an array of X", or "a function returning X".
   Type *Pointee = nullptr;
@@ -324,9 +334,13 @@ struct ArrayType : public Type {
 
 static bool isMemberPointer(StringView MangledName) {
   switch (MangledName.popFront()) {
+  case '$':
+    // This is probably an rvalue reference (e.g. $$Q), and you cannot have an
+    // rvalue reference to a member.
+    return false;
   case 'A':
     // 'A' indicates a reference, and you cannot have a reference to a member
-    // function or member variable.
+    // function or member.
     return false;
   case 'P':
   case 'Q':
@@ -449,17 +463,25 @@ static bool startsWithLocalScopePattern(StringView S) {
   return true;
 }
 
+static void outputName(OutputStream &OS, const Name *TheName);
+
 // Write a function or template parameter list.
-static void outputParameterList(OutputStream &OS, const ParamList &Params) {
-  if (!Params.Current) {
-    OS << "void";
+static void outputParameterList(OutputStream &OS, const ParamList &Params,
+                                bool EmptyAsVoid) {
+  if (!Params.Current && !Params.AliasName) {
+    if (EmptyAsVoid)
+      OS << "void";
     return;
   }
 
   const ParamList *Head = &Params;
   while (Head) {
-    Type::outputPre(OS, *Head->Current);
-    Type::outputPost(OS, *Head->Current);
+    if (Head->Current) {
+      Type::outputPre(OS, *Head->Current);
+      Type::outputPost(OS, *Head->Current);
+    } else if (Head->AliasName) {
+      outputName(OS, Head->AliasName);
+    }
 
     Head = Head->Next;
 
@@ -469,11 +491,11 @@ static void outputParameterList(OutputStream &OS, const ParamList &Params) {
 }
 
 static void outputTemplateParams(OutputStream &OS, const Name &TheName) {
-  if (!TheName.TemplateParams.Current)
+  if (!TheName.TemplateParams)
     return;
 
   OS << "<";
-  outputParameterList(OS, TheName.TemplateParams);
+  outputParameterList(OS, *TheName.TemplateParams, false);
   OS << ">";
 }
 
@@ -577,6 +599,12 @@ void Type::outputPre(OutputStream &OS) {
   case PrimTy::Uchar:
     OS << "unsigned char";
     break;
+  case PrimTy::Char16:
+    OS << "char16_t";
+    break;
+  case PrimTy::Char32:
+    OS << "char32_t";
+    break;
   case PrimTy::Short:
     OS << "short";
     break;
@@ -613,6 +641,9 @@ void Type::outputPre(OutputStream &OS) {
   case PrimTy::Ldouble:
     OS << "long double";
     break;
+  case PrimTy::Nullptr:
+    OS << "std::nullptr_t";
+    break;
   default:
     assert(false && "Invalid primitive type!");
   }
@@ -647,8 +678,10 @@ static void outputPointerIndicator(OutputStream &OS, PointerAffinity Affinity,
 
   if (Affinity == PointerAffinity::Pointer)
     OS << "*";
-  else
+  else if (Affinity == PointerAffinity::Reference)
     OS << "&";
+  else
+    OS << "&&";
 }
 
 void PointerType::outputPre(OutputStream &OS) {
@@ -659,9 +692,6 @@ void PointerType::outputPre(OutputStream &OS) {
   if (Quals & Q_Unaligned)
     OS << "__unaligned ";
 
-  PointerAffinity Affinity = (Prim == PrimTy::Ptr) ? PointerAffinity::Pointer
-                                                   : PointerAffinity::Reference;
-
   outputPointerIndicator(OS, Affinity, nullptr, Pointee);
 
   // FIXME: We should output this, but it requires updating lots of tests.
@@ -725,12 +755,21 @@ void FunctionType::outputPre(OutputStream &OS) {
 
 void FunctionType::outputPost(OutputStream &OS) {
   OS << "(";
-  outputParameterList(OS, Params);
+  outputParameterList(OS, Params, true);
   OS << ")";
   if (Quals & Q_Const)
     OS << " const";
   if (Quals & Q_Volatile)
     OS << " volatile";
+  if (Quals & Q_Restrict)
+    OS << " __restrict";
+  if (Quals & Q_Unaligned)
+    OS << " __unaligned";
+
+  if (RefKind == ReferenceKind::LValueRef)
+    OS << " &";
+  else if (RefKind == ReferenceKind::RValueRef)
+    OS << " &&";
 
   if (ReturnType)
     Type::outputPost(OS, *ReturnType);
@@ -820,7 +859,7 @@ private:
 
   ArrayType *demangleArrayType(StringView &MangledName);
 
-  ParamList demangleTemplateParameterList(StringView &MangledName);
+  ParamList *demangleTemplateParameterList(StringView &MangledName);
   ParamList demangleFunctionParameterList(StringView &MangledName);
 
   int demangleNumber(StringView &MangledName);
@@ -846,7 +885,8 @@ private:
   Name *demangleAnonymousNamespaceName(StringView &MangledName);
   Name *demangleLocallyScopedNamePiece(StringView &MangledName);
 
-  void demangleOperator(StringView &MangledName, Name *);
+  StringView demangleSimpleString(StringView &MangledName, bool Memorize);
+
   FuncClass demangleFunctionClass(StringView &MangledName);
   CallingConv demangleCallingConvention(StringView &MangledName);
   StorageClass demangleVariableStorageClass(StringView &MangledName);
@@ -931,7 +971,6 @@ Type *Demangler::demangleVariableEncoding(StringView &MangledName) {
   //                 ::= <type> <pointee-cvr-qualifiers> # pointers, references
   switch (Ty->Prim) {
   case PrimTy::Ptr:
-  case PrimTy::Ref:
   case PrimTy::MemberPtr: {
     Qualifiers ExtraChildQuals = Q_None;
     Ty->Quals =
@@ -1148,6 +1187,19 @@ Name *Demangler::demangleOperatorName(StringView &MangledName) {
       case '_':
         if (MangledName.consumeFront("L"))
           return " co_await";
+        if (MangledName.consumeFront("K")) {
+          size_t EndPos = MangledName.find('@');
+          if (EndPos == StringView::npos)
+            break;
+          StringView OpName = demangleSimpleString(MangledName, false);
+          size_t FullSize = OpName.size() + 3; // <space>""OpName
+          char *Buffer = Arena.allocUnalignedBuffer(FullSize);
+          Buffer[0] = ' ';
+          Buffer[1] = '"';
+          Buffer[2] = '"';
+          std::memcpy(Buffer + 3, OpName.begin(), OpName.size());
+          return {Buffer, FullSize};
+        }
       }
     }
     }
@@ -1161,20 +1213,31 @@ Name *Demangler::demangleOperatorName(StringView &MangledName) {
 }
 
 Name *Demangler::demangleSimpleName(StringView &MangledName, bool Memorize) {
+  StringView S = demangleSimpleString(MangledName, Memorize);
+  if (Error)
+    return nullptr;
+
   Name *Node = Arena.alloc<Name>();
+  Node->Str = S;
+  return Node;
+}
+
+StringView Demangler::demangleSimpleString(StringView &MangledName,
+                                           bool Memorize) {
+  StringView S;
   for (size_t i = 0; i < MangledName.size(); ++i) {
     if (MangledName[i] != '@')
       continue;
-    Node->Str = MangledName.substr(0, i);
+    S = MangledName.substr(0, i);
     MangledName = MangledName.dropFront(i + 1);
 
     if (Memorize)
-      memorizeString(Node->Str);
-    return Node;
+      memorizeString(S);
+    return S;
   }
 
   Error = true;
-  return nullptr;
+  return {};
 }
 
 Name *Demangler::demangleAnonymousNamespaceName(StringView &MangledName) {
@@ -1429,6 +1492,38 @@ Demangler::demangleQualifiers(StringView &MangledName) {
   return std::make_pair(Q_None, false);
 }
 
+static bool isTagType(StringView S) {
+  switch (S.front()) {
+  case 'T': // union
+  case 'U': // struct
+  case 'V': // class
+  case 'W': // enum
+    return true;
+  }
+  return false;
+}
+
+static bool isPointerType(StringView S) {
+  if (S.startsWith("$$Q")) // foo &&
+    return true;
+
+  switch (S.front()) {
+  case 'A': // foo &
+  case 'P': // foo *
+  case 'Q': // foo *const
+  case 'R': // foo *volatile
+  case 'S': // foo *const volatile
+    return true;
+  }
+  return false;
+}
+
+static bool isArrayType(StringView S) { return S[0] == 'Y'; }
+
+static bool isFunctionType(StringView S) {
+  return S.startsWith("$$A8@@") || S.startsWith("$$A6");
+}
+
 // <variable-type> ::= <type> <cvr-qualifiers>
 //                 ::= <type> <pointee-cvr-qualifiers> # pointers, references
 Type *Demangler::demangleType(StringView &MangledName,
@@ -1447,32 +1542,33 @@ Type *Demangler::demangleType(StringView &MangledName,
   }
 
   Type *Ty = nullptr;
-  switch (MangledName.front()) {
-  case 'T': // union
-  case 'U': // struct
-  case 'V': // class
-  case 'W': // enum
+  if (isTagType(MangledName))
     Ty = demangleClassType(MangledName);
-    break;
-  case 'A': // foo &
-  case 'P': // foo *
-  case 'Q': // foo *const
-  case 'R': // foo *volatile
-  case 'S': // foo *const volatile
+  else if (isPointerType(MangledName)) {
     if (!IsMemberKnown)
       IsMember = isMemberPointer(MangledName);
+
     if (IsMember)
       Ty = demangleMemberPointerType(MangledName);
     else
       Ty = demanglePointerType(MangledName);
-    break;
-  case 'Y':
+  } else if (isArrayType(MangledName))
     Ty = demangleArrayType(MangledName);
-    break;
-  default:
+  else if (isFunctionType(MangledName)) {
+    if (MangledName.consumeFront("$$A8@@"))
+      Ty = demangleFunctionType(MangledName, true, false);
+    else {
+      assert(MangledName.startsWith("$$A6"));
+      MangledName.consumeFront("$$A6");
+      Ty = demangleFunctionType(MangledName, false, false);
+    }
+  } else {
     Ty = demangleBasicType(MangledName);
-    break;
+    assert(Ty && !Error);
+    if (!Ty || Error)
+      return Ty;
   }
+
   Ty->Quals = Qualifiers(Ty->Quals | Quals);
   return Ty;
 }
@@ -1535,6 +1631,11 @@ Type *Demangler::demangleFunctionEncoding(StringView &MangledName) {
 Type *Demangler::demangleBasicType(StringView &MangledName) {
   Type *Ty = Arena.alloc<Type>();
 
+  if (MangledName.consumeFront("$$T")) {
+    Ty->Prim = PrimTy::Nullptr;
+    return Ty;
+  }
+
   switch (MangledName.popFront()) {
   case 'X':
     Ty->Prim = PrimTy::Void;
@@ -1593,11 +1694,21 @@ Type *Demangler::demangleBasicType(StringView &MangledName) {
     case 'W':
       Ty->Prim = PrimTy::Wchar;
       break;
+    case 'S':
+      Ty->Prim = PrimTy::Char16;
+      break;
+    case 'U':
+      Ty->Prim = PrimTy::Char32;
+      break;
     default:
-      assert(false);
+      Error = true;
+      return nullptr;
     }
     break;
   }
+  default:
+    Error = true;
+    return nullptr;
   }
   return Ty;
 }
@@ -1632,6 +1743,9 @@ UdtType *Demangler::demangleClassType(StringView &MangledName) {
 
 static std::pair<Qualifiers, PointerAffinity>
 demanglePointerCVQualifiers(StringView &MangledName) {
+  if (MangledName.consumeFront("$$Q"))
+    return std::make_pair(Q_None, PointerAffinity::RValueReference);
+
   switch (MangledName.popFront()) {
   case 'A':
     return std::make_pair(Q_None, PointerAffinity::Reference);
@@ -1655,11 +1769,10 @@ demanglePointerCVQualifiers(StringView &MangledName) {
 PointerType *Demangler::demanglePointerType(StringView &MangledName) {
   PointerType *Pointer = Arena.alloc<PointerType>();
 
-  PointerAffinity Affinity;
-  std::tie(Pointer->Quals, Affinity) = demanglePointerCVQualifiers(MangledName);
+  std::tie(Pointer->Quals, Pointer->Affinity) =
+      demanglePointerCVQualifiers(MangledName);
 
-  Pointer->Prim =
-      (Affinity == PointerAffinity::Pointer) ? PrimTy::Ptr : PrimTy::Ref;
+  Pointer->Prim = PrimTy::Ptr;
   if (MangledName.consumeFront("6")) {
     Pointer->Pointee = demangleFunctionType(MangledName, false, true);
     return Pointer;
@@ -1805,14 +1918,26 @@ ParamList Demangler::demangleFunctionParameterList(StringView &MangledName) {
   return {};
 }
 
-ParamList Demangler::demangleTemplateParameterList(StringView &MangledName) {
+ParamList *Demangler::demangleTemplateParameterList(StringView &MangledName) {
   ParamList *Head;
   ParamList **Current = &Head;
   while (!Error && !MangledName.startsWith('@')) {
-
     // Template parameter lists don't participate in back-referencing.
     *Current = Arena.alloc<ParamList>();
-    (*Current)->Current = demangleType(MangledName, QualifierMangleMode::Drop);
+
+    // Empty parameter pack.
+    if (MangledName.consumeFront("$S") || MangledName.consumeFront("$$V") ||
+        MangledName.consumeFront("$$$V")) {
+      if (!MangledName.startsWith('@'))
+        Error = true;
+      continue;
+    }
+
+    if (MangledName.consumeFront("$$Y"))
+      (*Current)->AliasName = demangleFullyQualifiedTypeName(MangledName);
+    else
+      (*Current)->Current =
+          demangleType(MangledName, QualifierMangleMode::Drop);
 
     Current = &(*Current)->Next;
   }
@@ -1823,7 +1948,7 @@ ParamList Demangler::demangleTemplateParameterList(StringView &MangledName) {
   // Template parameter lists cannot be variadic, so it can only be terminated
   // by @.
   if (MangledName.consumeFront('@'))
-    return *Head;
+    return Head;
   Error = true;
   return {};
 }
diff --git a/llvm/test/Demangle/ms-cxx11.test b/llvm/test/Demangle/ms-cxx11.test
new file mode 100644 (file)
index 0000000..ced5ebd
--- /dev/null
@@ -0,0 +1,146 @@
+; RUN: llvm-undname < %s | FileCheck %s
+
+; CHECK-NOT: Invalid mangled name
+
+?a@FTypeWithQuals@@3U?$S@$$A8@@BAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) const> FTypeWithQuals::a
+
+?b@FTypeWithQuals@@3U?$S@$$A8@@CAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) volatile> FTypeWithQuals::b
+
+?c@FTypeWithQuals@@3U?$S@$$A8@@IAAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) __restrict> FTypeWithQuals::c
+
+?d@FTypeWithQuals@@3U?$S@$$A8@@GBAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) const &> FTypeWithQuals::d
+
+?e@FTypeWithQuals@@3U?$S@$$A8@@GCAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) volatile &> FTypeWithQuals::e
+
+?f@FTypeWithQuals@@3U?$S@$$A8@@IGAAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) __restrict &> FTypeWithQuals::f
+
+?g@FTypeWithQuals@@3U?$S@$$A8@@HBAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) const &&> FTypeWithQuals::g
+
+?h@FTypeWithQuals@@3U?$S@$$A8@@HCAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) volatile &&> FTypeWithQuals::h
+
+?i@FTypeWithQuals@@3U?$S@$$A8@@IHAAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) __restrict &&> FTypeWithQuals::i
+
+?j@FTypeWithQuals@@3U?$S@$$A6AHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void)> FTypeWithQuals::j
+
+?k@FTypeWithQuals@@3U?$S@$$A8@@GAAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) &> FTypeWithQuals::k
+
+?l@FTypeWithQuals@@3U?$S@$$A8@@HAAHXZ@1@A
+; CHECK: struct FTypeWithQuals::S<int __cdecl(void) &&> FTypeWithQuals::l
+
+?Char16Var@@3_SA
+; CHECK: char16_t Char16Var
+
+?Char32Var@@3_UA
+; CHECK: char32_t Char32Var
+
+?LRef@@YAXAAH@Z
+; CHECK: void __cdecl LRef(int &)
+
+?RRef@@YAH$$QAH@Z
+; CHECK: int __cdecl RRef(int &&)
+
+?Null@@YAX$$T@Z
+; CHECK: void __cdecl Null(std::nullptr_t)
+
+?fun@PR18022@@YA?AU<unnamed-type-a>@1@U21@0@Z
+; CHECK: struct PR18022::<unnamed-type-a> __cdecl PR18022::fun(struct PR18022::<unnamed-type-a>, struct PR18022::<unnamed-type-a>)
+
+; First, we have the static local variable of type "<lambda_1>" inside of "define_lambda".
+; decltype(lambda), where lambda = [] { static int local=42; return 42; };
+?lambda@?1??define_lambda@@YAHXZ@4V<lambda_1>@?0??1@YAHXZ@A
+; CHECK: class `int __cdecl define_lambda(void)'::`1'::<lambda_1> `int __cdecl define_lambda(void)'::`2'::lambda
+
+; Next, we have the "operator()" for "<lambda_1>" which is inside of "define_lambda".
+??R<lambda_1>@?0??define_lambda@@YAHXZ@QBE@XZ
+; CHECK: __thiscall `int __cdecl define_lambda(void)'::`1'::<lambda_1>::operator()(void) const
+
+; Finally, we have the local which is inside of "<lambda_1>" which is inside of "define_lambda".
+?local@?2???R<lambda_1>@?0??define_lambda@@YAHXZ@QBE@XZ@4HA
+; CHECK: __thiscall `int __cdecl define_lambda(void)'::`1'::<lambda_1>::operator()(void) const
+
+??$use_lambda_arg@V<lambda_1>@?0??call_with_lambda_arg1@@YAXXZ@@@YAXV<lambda_1>@?0??call_with_lambda_arg1@@YAXXZ@@Z
+; CHECK: void __cdecl use_lambda_arg<class `void __cdecl call_with_lambda_arg1(void)'::`1'::<lambda_1>>(class `void __cdecl call_with_lambda_arg1(void)'::`1'::<lambda_1>)
+
+?foo@A@PR19361@@QIGAEXXZ
+; CHECK: void __thiscall PR19361::A::foo(void) __restrict &
+
+?foo@A@PR19361@@QIHAEXXZ
+; CHECK: void __thiscall PR19361::A::foo(void) __restrict &&
+
+??__K_deg@@YAHO@Z
+; CHECK: int __cdecl operator ""_deg(long double)
+
+??$templ_fun_with_pack@$S@@YAXXZ
+; CHECK: void __cdecl templ_fun_with_pack<>(void)
+
+??$templ_fun_with_ty_pack@$$$V@@YAXXZ
+; CHECK: void __cdecl templ_fun_with_ty_pack<>(void)
+??$templ_fun_with_ty_pack@$$V@@YAXXZ
+; CHECK: void __cdecl templ_fun_with_ty_pack<>(void)
+
+??$f@$$YAliasA@PR20047@@@PR20047@@YAXXZ
+; CHECK: void __cdecl PR20047::f<PR20047::AliasA>(void)
+
+?f@UnnamedType@@YAXAAU<unnamed-type-TD>@A@1@@Z
+; CHECK: void __cdecl UnnamedType::f(struct UnnamedType::A::<unnamed-type-TD> &)
+
+?f@UnnamedType@@YAXPAW4<unnamed-type-e>@?$B@H@1@@Z
+; CHECK: void __cdecl UnnamedType::f(enum UnnamedType::B<int>::<unnamed-type-e> *)
+
+??$f@W4<unnamed-type-E>@?1??g@PR24651@@YAXXZ@@PR24651@@YAXW4<unnamed-type-E>@?1??g@0@YAXXZ@@Z
+; We have a back-referencing problem here, we print `void __cdecl <unnamed-type-E>::g(void)`
+; for the second occurrence of g.
+; FIXME: void __cdecl PR24651::f<enum `void __cdecl PR24651::g(void)'::`2'::<unnamed-type-E>>(enum `void __cdecl PR24651::g(void)'::`2'::<unnamed-type-E>)
+
+??$f@T<unnamed-type-$S1>@PR18204@@@PR18204@@YAHPAT<unnamed-type-$S1>@0@@Z
+; FIXME: int __cdecl PR18204::f<union PR18204::<unnamed-type-$S1>>(union PR18204::<unnamed-type-$S1> *)
+
+??R<lambda_0>@?0??PR26105@@YAHXZ@QBE@H@Z
+; CHECK: __thiscall `int __cdecl PR26105(void)'::`1'::<lambda_0>::operator()(int) const
+
+??R<lambda_1>@?0???R<lambda_0>@?0??PR26105@@YAHXZ@QBE@H@Z@QBE@H@Z
+; CHECK: __thiscall `__thiscall `int __cdecl PR26105(void)'::`1'::<lambda_0>::operator()(int) const'::`1'::<lambda_1>::operator()(int) const
+
+?unaligned_foo1@@YAPFAHXZ
+; CHECK: int __unaligned * __cdecl unaligned_foo1(void)
+
+?unaligned_foo2@@YAPFAPFAHXZ
+; CHECK: int __unaligned *__unaligned * __cdecl unaligned_foo2(void)
+
+?unaligned_foo3@@YAHXZ
+; CHECK: int __cdecl unaligned_foo3(void)
+
+?unaligned_foo4@@YAXPFAH@Z
+; CHECK: void __cdecl unaligned_foo4(int __unaligned *)
+
+?unaligned_foo5@@YAXPIFAH@Z
+; CHECK: void __cdecl unaligned_foo5(int __unaligned *__restrict)
+
+??$unaligned_foo6@PAH@@YAPAHPAH@Z
+; CHECK: int * __cdecl unaligned_foo6<int *>(int *)
+
+??$unaligned_foo6@PFAH@@YAPFAHPFAH@Z
+; CHECK: int __unaligned * __cdecl unaligned_foo6<int __unaligned *>(int __unaligned *)
+
+?unaligned_foo8@unaligned_foo8_S@@QFCEXXZ
+; CHECK: void __thiscall unaligned_foo8_S::unaligned_foo8(void) volatile __unaligned
+
+??R<lambda_1>@x@A@PR31197@@QBE@XZ
+; CHECK: __thiscall PR31197::A::x::<lambda_1>::operator()(void) const
+
+?white@?1???R<lambda_1>@x@A@PR31197@@QBE@XZ@4HA
+; CHECK: int `__thiscall PR31197::A::x::<lambda_1>::operator()(void) const'::`2'::white
+
+?f@@YAXW4<unnamed-enum-enumerator>@@@Z
+; CHECK: void __cdecl f(enum <unnamed-enum-enumerator>)