|  | //===-------------- ItaniumManglingCanonicalizerTest.cpp ------------------===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is dual licensed under the MIT and the University of Illinois Open | 
|  | // Source Licenses. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/Support/ItaniumManglingCanonicalizer.h" | 
|  | #include "llvm/ADT/ArrayRef.h" | 
|  | #include "llvm/ADT/StringRef.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | #include <cstdlib> | 
|  | #include <map> | 
|  | #include <vector> | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | using EquivalenceError = llvm::ItaniumManglingCanonicalizer::EquivalenceError; | 
|  | using FragmentKind = llvm::ItaniumManglingCanonicalizer::FragmentKind; | 
|  |  | 
|  | struct Equivalence { | 
|  | FragmentKind Kind; | 
|  | llvm::StringRef First; | 
|  | llvm::StringRef Second; | 
|  | }; | 
|  |  | 
|  | // A set of manglings that should all be considered equivalent. | 
|  | using EquivalenceClass = std::vector<llvm::StringRef>; | 
|  |  | 
|  | struct Testcase { | 
|  | // A set of equivalences to register. | 
|  | std::vector<Equivalence> Equivalences; | 
|  | // A set of distinct equivalence classes created by registering the | 
|  | // equivalences. | 
|  | std::vector<EquivalenceClass> Classes; | 
|  | }; | 
|  |  | 
|  | // A function that returns a set of test cases. | 
|  | static std::vector<Testcase> getTestcases() { | 
|  | return { | 
|  | // Three different manglings for std::string (old libstdc++, new libstdc++, | 
|  | // libc++). | 
|  | { | 
|  | { | 
|  | {FragmentKind::Type, "Ss", | 
|  | "NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE"}, | 
|  | {FragmentKind::Type, "Ss", | 
|  | "NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"}, | 
|  | }, | 
|  | { | 
|  | {"_Z1fv"}, | 
|  | {"_Z1fSs", | 
|  | "_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE", | 
|  | "_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"}, | 
|  | {"_ZNKSs4sizeEv", | 
|  | "_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE4sizeEv", | 
|  | "_ZNKSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE4sizeEv"}, | 
|  | } | 
|  | }, | 
|  |  | 
|  | // Check that substitutions are properly handled. | 
|  | { | 
|  | { | 
|  | // ::X <-> ::N::X<int> | 
|  | {FragmentKind::Type, "1X", "N1N1XIiEE"}, | 
|  | // ::T<T<int, int>, T<int, int>> <-> T<int> | 
|  | {FragmentKind::Type, "1TIS_IiiES0_E", "1TIiE"}, | 
|  | // A::B::foo <-> AB::foo | 
|  | {FragmentKind::Name, "N1A1B3fooE", "N2AB3fooE"}, | 
|  | }, | 
|  | { | 
|  | {"_Z1f1XPS_RS_", "_Z1fN1N1XIiEEPS1_RS1_"}, | 
|  | {"_ZN1A1B3fooE1TIS1_IiiES2_EPS3_RS3_", "_ZN2AB3fooE1TIiEPS1_RS1_"}, | 
|  | } | 
|  | }, | 
|  |  | 
|  | // Check that nested equivalences are properly handled. | 
|  | { | 
|  | { | 
|  | // std::__1::char_traits == std::__cxx11::char_traits | 
|  | // (Note that this is unused and should make no difference, | 
|  | // but it should not cause us to fail to match up the cases | 
|  | // below.) | 
|  | {FragmentKind::Name, | 
|  | "NSt3__111char_traitsE", | 
|  | "NSt7__cxx1111char_traitsE"}, | 
|  | // std::__1::allocator == std::allocator | 
|  | {FragmentKind::Name, | 
|  | "NSt3__19allocatorE", | 
|  | "Sa"}, // "Sa" is not strictly a <name> but we accept it as one. | 
|  | // std::__1::vector == std::vector | 
|  | {FragmentKind::Name, | 
|  | "St6vector", | 
|  | "NSt3__16vectorE"}, | 
|  | // std::__1::basic_string< | 
|  | //   char | 
|  | //   std::__1::char_traits<char>, | 
|  | //   std::__1::allocator<char>> == | 
|  | // std::__cxx11::basic_string< | 
|  | //   char, | 
|  | //   std::char_traits<char>, | 
|  | //   std::allocator<char>> | 
|  | {FragmentKind::Type, | 
|  | "NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE", | 
|  | "NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"}, | 
|  | // X<A> <-> X<B> | 
|  | {FragmentKind::Type, "1XI1AE", "1XI1BE"}, | 
|  | // X <-> Y | 
|  | {FragmentKind::Name, "1X", "1Y"}, | 
|  | }, | 
|  | { | 
|  | // f(std::string) | 
|  | {"_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE", | 
|  | "_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"}, | 
|  | // f(std::vector<int>) | 
|  | {"_Z1fSt6vectorIiSaIiEE", "_Z1fNSt3__16vectorIiNS_9allocatorIiEEEE"}, | 
|  | // f(X<A>), f(X<B>), f(Y<A>), f(Y<B>) | 
|  | {"_Z1f1XI1AE", "_Z1f1XI1BE", "_Z1f1YI1AE", "_Z1f1YI1BE"}, | 
|  | // f(X<C>), f(Y<C>) | 
|  | {"_Z1f1XI1CE", "_Z1f1YI1CE"}, | 
|  | } | 
|  | }, | 
|  |  | 
|  | // Check namespace equivalences. | 
|  | { | 
|  | { | 
|  | // std::__1 == std::__cxx11 | 
|  | {FragmentKind::Name, "St3__1", "St7__cxx11"}, | 
|  | // std::__1::allocator == std::allocator | 
|  | {FragmentKind::Name, "NSt3__19allocatorE", "Sa"}, | 
|  | // std::vector == std::__1::vector | 
|  | {FragmentKind::Name, "St6vector", "NSt3__16vectorE"}, | 
|  | // std::__cxx11::char_traits == std::char_traits | 
|  | // (This indirectly means that std::__1::char_traits == std::char_traits, | 
|  | // due to the std::__cxx11 == std::__1 equivalence, which is what we rely | 
|  | // on below.) | 
|  | {FragmentKind::Name, "NSt7__cxx1111char_traitsE", "St11char_traits"}, | 
|  | }, | 
|  | { | 
|  | // f(std::foo) | 
|  | {"_Z1fNSt7__cxx113fooE", | 
|  | "_Z1fNSt3__13fooE"}, | 
|  | // f(std::string) | 
|  | {"_Z1fNSt7__cxx1111char_traitsIcEE", | 
|  | "_Z1fNSt3__111char_traitsIcEE", | 
|  | "_Z1fSt11char_traitsIcE"}, | 
|  | // f(std::string) | 
|  | {"_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE", | 
|  | "_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"}, | 
|  | // f(std::vector<int>) | 
|  | {"_Z1fSt6vectorIiSaIiEE", "_Z1fNSt3__16vectorIiNS_9allocatorIiEEEE"}, | 
|  | } | 
|  | }, | 
|  |  | 
|  | // Check namespace equivalences for namespace 'std'. We support using 'St' | 
|  | // for this, despite it not technically being a <name>. | 
|  | { | 
|  | { | 
|  | // std::__1 == std | 
|  | {FragmentKind::Name, "St3__1", "St"}, | 
|  | // std::__1 == std::__cxx11 | 
|  | {FragmentKind::Name, "St3__1", "St7__cxx11"}, | 
|  | // FIXME: Should a 'std' equivalence also cover the predefined | 
|  | // substitutions? | 
|  | // std::__1::allocator == std::allocator | 
|  | {FragmentKind::Name, "NSt3__19allocatorE", "Sa"}, | 
|  | }, | 
|  | { | 
|  | {"_Z1fSt3foo", "_Z1fNSt3__13fooE", "_Z1fNSt7__cxx113fooE"}, | 
|  | {"_Z1fNSt3bar3bazE", "_Z1fNSt3__13bar3bazE"}, | 
|  | // f(std::string) | 
|  | {"_Z1fNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE", | 
|  | "_Z1fNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE"}, | 
|  | // f(std::vector<int>) | 
|  | {"_Z1fSt6vectorIiSaIiEE", "_Z1fNSt3__16vectorIiNS_9allocatorIiEEEE"}, | 
|  | } | 
|  | }, | 
|  |  | 
|  | // Check mutually-recursive equivalences. | 
|  | { | 
|  | { | 
|  | {FragmentKind::Type, "1A", "1B"}, | 
|  | {FragmentKind::Type, "1A", "1C"}, | 
|  | {FragmentKind::Type, "1D", "1B"}, | 
|  | {FragmentKind::Type, "1C", "1E"}, | 
|  | }, | 
|  | { | 
|  | {"_Z1f1A", "_Z1f1B", "_Z1f1C", "_Z1f1D", "_Z1f1E"}, | 
|  | {"_Z1f1F"}, | 
|  | } | 
|  | }, | 
|  |  | 
|  | // Check <encoding>s. | 
|  | { | 
|  | { | 
|  | {FragmentKind::Encoding, "1fv", "1gv"}, | 
|  | }, | 
|  | { | 
|  | // f(void) -> g(void) | 
|  | {"_Z1fv", "_Z1gv"}, | 
|  | // static local 'n' in f(void) -> static local 'n' in g(void) | 
|  | {"_ZZ1fvE1n", "_ZZ1gvE1n"}, | 
|  | } | 
|  | }, | 
|  |  | 
|  | // Corner case: the substitution can appear within its own expansion. | 
|  | { | 
|  | { | 
|  | // X <-> Y<X> | 
|  | {FragmentKind::Type, "1X", "1YI1XE"}, | 
|  | // A<B> <-> B | 
|  | {FragmentKind::Type, "1AI1BE", "1B"}, | 
|  | }, | 
|  | { | 
|  | // f(X) == f(Y<X>) == f(Y<Y<X>>) == f(Y<Y<Y<X>>>) | 
|  | {"_Z1f1X", "_Z1f1YI1XE", "_Z1f1YIS_I1XEE", "_Z1f1YIS_IS_I1XEEE"}, | 
|  | // f(B) == f(A<B>) == f(A<A<B>>) == f(A<A<A<B>>>) | 
|  | {"_Z1f1B", "_Z1f1AI1BE", "_Z1f1AIS_I1BEE", "_Z1f1AIS_IS_I1BEEE"}, | 
|  | } | 
|  | }, | 
|  |  | 
|  | // Redundant equivalences are accepted (and have no effect). | 
|  | { | 
|  | { | 
|  | {FragmentKind::Name, "3std", "St"}, | 
|  | {FragmentKind::Name, "1X", "1Y"}, | 
|  | {FragmentKind::Name, "N1X1ZE", "N1Y1ZE"}, | 
|  | }, | 
|  | {} | 
|  | }, | 
|  |  | 
|  | // Check that ctor and dtor variants are considered distinct. | 
|  | { | 
|  | {}, | 
|  | {{"_ZN1XC1Ev"}, {"_ZN1XC2Ev"}, {"_ZN1XD1Ev"}, {"_ZN1XD2Ev"}} | 
|  | }, | 
|  |  | 
|  | // Ensure array types with and without bounds are handled properly. | 
|  | { | 
|  | { | 
|  | {FragmentKind::Type, "A_i", "A1_f"}, | 
|  | }, | 
|  | { | 
|  | {"_Z1fRA_i", "_Z1fRA_i", "_Z1fRA1_f"}, | 
|  | {"_Z1fRA1_i"}, {"_Z1fRA_f"}, | 
|  | } | 
|  | }, | 
|  | }; | 
|  | } | 
|  |  | 
|  | // A function to get a set of test cases for forward template references. | 
|  | static std::vector<Testcase> getForwardTemplateReferenceTestcases() { | 
|  | return { | 
|  | // ForwardTemplateReference does not support canonicalization. | 
|  | // FIXME: We should consider ways of fixing this, perhaps by eliminating | 
|  | // the ForwardTemplateReference node with a tree transformation. | 
|  | { | 
|  | { | 
|  | // X::operator T() <with T = A> == Y::operator T() <with T = A> | 
|  | {FragmentKind::Encoding, "N1XcvT_I1AEEv", "N1YcvT_I1AEEv"}, | 
|  | // A == B | 
|  | {FragmentKind::Name, "1A", "1B"}, | 
|  | }, | 
|  | { | 
|  | // All combinations result in unique equivalence classes. | 
|  | {"_ZN1XcvT_I1AEEv"}, | 
|  | {"_ZN1XcvT_I1BEEv"}, | 
|  | {"_ZN1YcvT_I1AEEv"}, | 
|  | {"_ZN1YcvT_I1BEEv"}, | 
|  | // Even giving the same string twice gives a new class. | 
|  | {"_ZN1XcvT_I1AEEv"}, | 
|  | } | 
|  | }, | 
|  | }; | 
|  | } | 
|  |  | 
|  | template<bool CanonicalizeFirst> | 
|  | static void testTestcases(ArrayRef<Testcase> Testcases) { | 
|  | for (const auto &Testcase : Testcases) { | 
|  | llvm::ItaniumManglingCanonicalizer Canonicalizer; | 
|  | for (const auto &Equiv : Testcase.Equivalences) { | 
|  | auto Result = | 
|  | Canonicalizer.addEquivalence(Equiv.Kind, Equiv.First, Equiv.Second); | 
|  | EXPECT_EQ(Result, EquivalenceError::Success) | 
|  | << "couldn't add equivalence between " << Equiv.First << " and " | 
|  | << Equiv.Second; | 
|  | } | 
|  |  | 
|  | using CanonKey = llvm::ItaniumManglingCanonicalizer::Key; | 
|  |  | 
|  | std::map<const EquivalenceClass*, CanonKey> Keys; | 
|  | if (CanonicalizeFirst) | 
|  | for (const auto &Class : Testcase.Classes) | 
|  | Keys.insert({&Class, Canonicalizer.canonicalize(*Class.begin())}); | 
|  |  | 
|  | std::map<CanonKey, llvm::StringRef> Found; | 
|  | for (const auto &Class : Testcase.Classes) { | 
|  | CanonKey ClassKey = Keys[&Class]; | 
|  | for (llvm::StringRef Str : Class) { | 
|  | // Force a copy to be made when calling lookup to test that it doesn't | 
|  | // retain any part of the provided string. | 
|  | CanonKey ThisKey = CanonicalizeFirst | 
|  | ? Canonicalizer.lookup(std::string(Str)) | 
|  | : Canonicalizer.canonicalize(Str); | 
|  | EXPECT_NE(ThisKey, CanonKey()) << "couldn't canonicalize " << Str; | 
|  | if (ClassKey) { | 
|  | EXPECT_EQ(ThisKey, ClassKey) | 
|  | << Str << " not in the same class as " << *Class.begin(); | 
|  | } else { | 
|  | ClassKey = ThisKey; | 
|  | } | 
|  | } | 
|  | EXPECT_TRUE(Found.insert({ClassKey, *Class.begin()}).second) | 
|  | << *Class.begin() << " is in the same class as " << Found[ClassKey]; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(ItaniumManglingCanonicalizerTest, TestCanonicalize) { | 
|  | testTestcases<false>(getTestcases()); | 
|  | } | 
|  |  | 
|  | TEST(ItaniumManglingCanonicalizerTest, TestLookup) { | 
|  | testTestcases<true>(getTestcases()); | 
|  | } | 
|  |  | 
|  | TEST(ItaniumManglingCanonicalizerTest, TestForwardTemplateReference) { | 
|  | // lookup(...) after canonicalization (intentionally) returns different | 
|  | // values for this testcase. | 
|  | testTestcases<false>(getForwardTemplateReferenceTestcases()); | 
|  | } | 
|  |  | 
|  |  | 
|  | TEST(ItaniumManglingCanonicalizerTest, TestInvalidManglings) { | 
|  | llvm::ItaniumManglingCanonicalizer Canonicalizer; | 
|  | EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "", "1X"), | 
|  | EquivalenceError::InvalidFirstMangling); | 
|  | EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1X", "1ab"), | 
|  | EquivalenceError::InvalidSecondMangling); | 
|  | EXPECT_EQ(Canonicalizer.canonicalize("_Z3fooE"), | 
|  | llvm::ItaniumManglingCanonicalizer::Key()); | 
|  | EXPECT_EQ(Canonicalizer.canonicalize("foo"), | 
|  | llvm::ItaniumManglingCanonicalizer::Key()); | 
|  |  | 
|  | // A reference to a template parameter ('T_' etc) cannot appear in a <name>, | 
|  | // because we don't have template arguments to bind to it. (The arguments in | 
|  | // an 'I ... E' construct in the <name> aren't registered as | 
|  | // backreferenceable arguments in this sense, because they're not part of | 
|  | // the template argument list of an <encoding>. | 
|  | EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Name, "N1XcvT_I1AEE", | 
|  | "1f"), | 
|  | EquivalenceError::InvalidFirstMangling); | 
|  | } | 
|  |  | 
|  | TEST(ItaniumManglingCanonicalizerTest, TestBadEquivalenceOrder) { | 
|  | llvm::ItaniumManglingCanonicalizer Canonicalizer; | 
|  | EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "N1P1XE", "N1Q1XE"), | 
|  | EquivalenceError::Success); | 
|  | EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1P", "1Q"), | 
|  | EquivalenceError::ManglingAlreadyUsed); | 
|  |  | 
|  | EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "N1C1XE", "N1A1YE"), | 
|  | EquivalenceError::Success); | 
|  | EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1A", "1B"), | 
|  | EquivalenceError::Success); | 
|  | EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1C", "1D"), | 
|  | EquivalenceError::Success); | 
|  | EXPECT_EQ(Canonicalizer.addEquivalence(FragmentKind::Type, "1B", "1D"), | 
|  | EquivalenceError::ManglingAlreadyUsed); | 
|  | } | 
|  |  | 
|  | } // end anonymous namespace |