Skip to content

[lldb] Refactor TypeQuery::ContextMatches #99305

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed

Conversation

labath
Copy link
Collaborator

@labath labath commented Jul 17, 2024

This is a preparatory step for teaching the function about anonymous namespaces. It started out as a way to remove the assumption that the pattern and target contexts must be of the same length -- that's will not be correct with anonymous namespaces, and probably isn't even correct right now for AnyModule matches.

To make that work, I needed to reverse the direction of the matching (since partial matches can ignore outermost contexts). This in turn made me realise that reversing the greedy AnyModule matching algorithm would not preserve current behavior, and AnyModule would greedily consume everything, and leave nothing for the exact module name matches that are before it (the same thing could happen in the original algorithm with modules that come after an AnyModule pattern, but I guess we just don't make queries like that).

That's why the bulk of this patch is concerned with implementing a more correct (maybe? I'm assuming the intention was to provide glob-like matching) matching for AnyModule patterns. The algorithm is technically quadratic, but that's probably fine as I don't think we have deeply nested modules or many wildcard matches.

This is a preparatory step for teaching the function about anonymous
namespaces. It started out as a way to remove the assumption that the
pattern and target contexts must be of the same length -- that's will
not be correct with anonymous namespaces, and probably isn't even
correct right now for AnyModule matches.

To make that work, I needed to reverse the direction of the matching
(since partial matches can ignore outermost contexts). This in turn made
me realise that reversing the greedy AnyModule matching algorithm would
not preserve current behavior, and AnyModule would greedily consume
everything, and leave nothing for the exact module name matches that
are before it (the same thing could happen in the original algorithm
with modules that come after an AnyModule pattern, but I guess we just
don't make queries like that).

That's why the bulk of this patch is concerned with implementing a more
correct (maybe? I'm assuming the intention was to provide glob-like
matching) matching for AnyModule patterns. The algorithm is technically
quadratic, but that's probably fine as I don't think we have deeply
nested modules or many wildcard matches.
@labath labath requested review from clayborg and Michael137 July 17, 2024 10:49
@labath labath requested a review from JDevlieghere as a code owner July 17, 2024 10:49
@llvmbot llvmbot added the lldb label Jul 17, 2024
@llvmbot
Copy link
Member

llvmbot commented Jul 17, 2024

@llvm/pr-subscribers-lldb

Author: Pavel Labath (labath)

Changes

This is a preparatory step for teaching the function about anonymous namespaces. It started out as a way to remove the assumption that the pattern and target contexts must be of the same length -- that's will not be correct with anonymous namespaces, and probably isn't even correct right now for AnyModule matches.

To make that work, I needed to reverse the direction of the matching (since partial matches can ignore outermost contexts). This in turn made me realise that reversing the greedy AnyModule matching algorithm would not preserve current behavior, and AnyModule would greedily consume everything, and leave nothing for the exact module name matches that are before it (the same thing could happen in the original algorithm with modules that come after an AnyModule pattern, but I guess we just don't make queries like that).

That's why the bulk of this patch is concerned with implementing a more correct (maybe? I'm assuming the intention was to provide glob-like matching) matching for AnyModule patterns. The algorithm is technically quadratic, but that's probably fine as I don't think we have deeply nested modules or many wildcard matches.


Full diff: https://github.com/llvm/llvm-project/pull/99305.diff

3 Files Affected:

  • (modified) lldb/include/lldb/Symbol/Type.h (-5)
  • (modified) lldb/source/Symbol/Type.cpp (+86-42)
  • (modified) lldb/unittests/Symbol/TestType.cpp (+83-35)
diff --git a/lldb/include/lldb/Symbol/Type.h b/lldb/include/lldb/Symbol/Type.h
index c6f30cde81867..365f600343e1d 100644
--- a/lldb/include/lldb/Symbol/Type.h
+++ b/lldb/include/lldb/Symbol/Type.h
@@ -65,11 +65,6 @@ struct CompilerContext {
 llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
                               const CompilerContext &rhs);
 
-/// Match \p context_chain against \p pattern, which may contain "Any"
-/// kinds. The \p context_chain should *not* contain any "Any" kinds.
-bool contextMatches(llvm::ArrayRef<CompilerContext> context_chain,
-                    llvm::ArrayRef<CompilerContext> pattern);
-
 FLAGS_ENUM(TypeQueryOptions){
     e_none = 0u,
     /// If set, TypeQuery::m_context contains an exact context that must match
diff --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp
index e76574795733f..67ed4a2e41f49 100644
--- a/lldb/source/Symbol/Type.cpp
+++ b/lldb/source/Symbol/Type.cpp
@@ -6,7 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include <algorithm>
 #include <cstdio>
+#include <iterator>
 #include <optional>
 
 #include "lldb/Core/Module.h"
@@ -30,6 +32,7 @@
 #include "lldb/Target/Process.h"
 #include "lldb/Target/Target.h"
 #include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-private-enumerations.h"
 
 #include "llvm/ADT/StringRef.h"
 
@@ -43,35 +46,6 @@ llvm::raw_ostream &lldb_private::operator<<(llvm::raw_ostream &os,
   return os << lldb_stream.GetString();
 }
 
-bool lldb_private::contextMatches(llvm::ArrayRef<CompilerContext> context_chain,
-                                  llvm::ArrayRef<CompilerContext> pattern) {
-  auto ctx = context_chain.begin();
-  auto ctx_end = context_chain.end();
-  for (const CompilerContext &pat : pattern) {
-    // Early exit if the pattern is too long.
-    if (ctx == ctx_end)
-      return false;
-    if (*ctx != pat) {
-      // Skip any number of module matches.
-      if (pat.kind == CompilerContextKind::AnyModule) {
-        // Greedily match 0..n modules.
-        ctx = std::find_if(ctx, ctx_end, [](const CompilerContext &ctx) {
-          return ctx.kind != CompilerContextKind::Module;
-        });
-        continue;
-      }
-      // See if there is a kind mismatch; they should have 1 bit in common.
-      if (((uint16_t)ctx->kind & (uint16_t)pat.kind) == 0)
-        return false;
-      // The name is ignored for AnyModule, but not for AnyType.
-      if (pat.kind != CompilerContextKind::AnyModule && ctx->name != pat.name)
-        return false;
-    }
-    ++ctx;
-  }
-  return true;
-}
-
 static CompilerContextKind ConvertTypeClass(lldb::TypeClass type_class) {
   if (type_class == eTypeClassAny)
     return CompilerContextKind::AnyType;
@@ -153,19 +127,89 @@ void TypeQuery::SetLanguages(LanguageSet languages) {
 
 bool TypeQuery::ContextMatches(
     llvm::ArrayRef<CompilerContext> context_chain) const {
-  if (GetExactMatch() || context_chain.size() == m_context.size())
-    return ::contextMatches(context_chain, m_context);
-
-  // We don't have an exact match, we need to bottom m_context.size() items to
-  // match for a successful lookup.
-  if (context_chain.size() < m_context.size())
-    return false; // Not enough items in context_chain to allow for a match.
-
-  size_t compare_count = context_chain.size() - m_context.size();
-  return ::contextMatches(
-      llvm::ArrayRef<CompilerContext>(context_chain.data() + compare_count,
-                                      m_context.size()),
-      m_context);
+  auto ctx = context_chain.rbegin(), ctx_end = context_chain.rend();
+  for (auto pat = m_context.rbegin(), pat_end = m_context.rend();
+       pat != pat_end;) {
+
+    // Handle AnyModule matches. These are tricky as they can match any number
+    // of modules.
+    if (pat->kind == CompilerContextKind::AnyModule) {
+      // Successive wildcards are equivalent to a single wildcard.
+      while (pat->kind == CompilerContextKind::AnyModule)
+        ++pat;
+
+      // [ctx, ctx_module_end) is the range of entries that may be matched by
+      // our wildcard.
+      auto ctx_module_end =
+          std::find_if(ctx, ctx_end, [](const CompilerContext &c) {
+            return c.kind != CompilerContextKind::Module;
+          });
+
+      // [pat, exact_pat_end) is the range of exact module match patterns. If
+      // it's not empty, we need to make sure our wildcard does not consume
+      // entries matched by those.
+      auto exact_pat_end =
+          std::find_if(pat, pat_end, [](const CompilerContext &p) {
+            return (p.kind & CompilerContextKind::AnyModule) !=
+                   CompilerContextKind::Module;
+          });
+
+      if (pat == exact_pat_end) {
+        // No exact matches, just consume everything.
+        ctx = ctx_module_end;
+        continue;
+      }
+
+      // We have a non-empty module sequence after the wildcard. Now we need to
+      // look at what comes *after* that. If that's another wildcard, we want
+      // *this* wildcard to match as little as possible to give the other
+      // wildcard (and whatever comes after it) as much freedom as possible. If
+      // it's *not* another wildcard (we've reached the end of the pattern, or
+      // we have some non-module patterns after this), we want to match as much
+      // as possible, as there will be nothing left to match any remaining
+      // module entries.
+      bool greedy_match = exact_pat_end == pat_end ||
+                          exact_pat_end->kind != CompilerContextKind::AnyModule;
+
+      auto pred = [](const CompilerContext &c, const CompilerContext &p) {
+        return c.name == p.name;
+      };
+      auto pos =
+          greedy_match
+              ? std::find_end(ctx, ctx_module_end, pat, exact_pat_end, pred)
+              : std::search(ctx, ctx_module_end, pat, exact_pat_end, pred);
+
+      if (pos == ctx_module_end)
+        return false; // Matching failed.
+
+      // We've successfully matched the wildcard and the exact-module sequence
+      // that comes after it.
+      ctx = std::next(pos, std::distance(pat, exact_pat_end));
+      pat = exact_pat_end;
+      continue;
+    }
+
+    if (ctx == ctx_end)
+      return false; // Pattern too long.
+
+    // See if there is a kind mismatch; they should have 1 bit in common.
+    if ((ctx->kind & pat->kind) == CompilerContextKind())
+      return false;
+
+    if (ctx->name != pat->name)
+      return false;
+
+    ++ctx;
+    ++pat;
+  }
+
+  // At this point, we have exhausted the pattern and we have a partial match at
+  // least. If that's all we're looking for, we're done.
+  if (!GetExactMatch())
+    return true;
+
+  // We have an exact match if we've exhausted the target context as well.
+  return ctx == ctx_end;
 }
 
 bool TypeQuery::LanguageMatches(lldb::LanguageType language) const {
diff --git a/lldb/unittests/Symbol/TestType.cpp b/lldb/unittests/Symbol/TestType.cpp
index 79201d6ba2e59..2814920bec3f5 100644
--- a/lldb/unittests/Symbol/TestType.cpp
+++ b/lldb/unittests/Symbol/TestType.cpp
@@ -7,13 +7,16 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 #include "lldb/Symbol/Type.h"
 #include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-private-enumerations.h"
 
 using namespace lldb;
 using namespace lldb_private;
+using testing::Not;
 
 TEST(Type, GetTypeScopeAndBasename) {
   EXPECT_EQ(Type::GetTypeScopeAndBasename("int"),
@@ -47,40 +50,85 @@ TEST(Type, GetTypeScopeAndBasename) {
   EXPECT_EQ(Type::GetTypeScopeAndBasename("foo<::bar"), std::nullopt);
 }
 
+namespace {
+MATCHER_P(Matches, pattern, "") {
+  TypeQuery query(pattern, TypeQueryOptions::e_none);
+  return query.ContextMatches(arg);
+}
+} // namespace
+
 TEST(Type, CompilerContextPattern) {
-  std::vector<CompilerContext> mmc = {
-      {CompilerContextKind::Module, ConstString("A")},
-      {CompilerContextKind::Module, ConstString("B")},
-      {CompilerContextKind::ClassOrStruct, ConstString("S")}};
-  std::vector<CompilerContext> mc = {
-      {CompilerContextKind::Module, ConstString("A")},
-      {CompilerContextKind::ClassOrStruct, ConstString("S")}};
-  std::vector<CompilerContext> mac = {
-      {CompilerContextKind::Module, ConstString("A")},
-      {CompilerContextKind::AnyModule, ConstString("*")},
-      {CompilerContextKind::ClassOrStruct, ConstString("S")}};
-  EXPECT_TRUE(contextMatches(mmc, mac));
-  EXPECT_TRUE(contextMatches(mc, mac));
-  EXPECT_FALSE(contextMatches(mac, mc));
-  std::vector<CompilerContext> mmmc = {
-      {CompilerContextKind::Module, ConstString("A")},
-      {CompilerContextKind::Module, ConstString("B")},
-      {CompilerContextKind::Module, ConstString("C")},
-      {CompilerContextKind::ClassOrStruct, ConstString("S")}};
-  EXPECT_TRUE(contextMatches(mmmc, mac));
-  std::vector<CompilerContext> mme = {
-      {CompilerContextKind::Module, ConstString("A")},
-      {CompilerContextKind::Module, ConstString("B")},
-      {CompilerContextKind::Enum, ConstString("S")}};
-  std::vector<CompilerContext> mma = {
-      {CompilerContextKind::Module, ConstString("A")},
-      {CompilerContextKind::Module, ConstString("B")},
-      {CompilerContextKind::AnyType, ConstString("S")}};
-  EXPECT_TRUE(contextMatches(mme, mma));
-  EXPECT_TRUE(contextMatches(mmc, mma));
-  std::vector<CompilerContext> mme2 = {
-      {CompilerContextKind::Module, ConstString("A")},
-      {CompilerContextKind::Module, ConstString("B")},
-      {CompilerContextKind::Enum, ConstString("S2")}};
-  EXPECT_FALSE(contextMatches(mme2, mma));
+  const CompilerContext any_module(CompilerContextKind::AnyModule,
+                                   ConstString("*"));
+  auto make_module = [](llvm::StringRef name) {
+    return CompilerContext(CompilerContextKind::Module, ConstString(name));
+  };
+  auto make_class = [](llvm::StringRef name) {
+    return CompilerContext(CompilerContextKind::ClassOrStruct,
+                           ConstString(name));
+  };
+  auto make_any_type = [](llvm::StringRef name) {
+    return CompilerContext(CompilerContextKind::AnyType, ConstString(name));
+  };
+  auto make_enum = [](llvm::StringRef name) {
+    return CompilerContext(CompilerContextKind::Enum, ConstString(name));
+  };
+  auto make_namespace = [](llvm::StringRef name) {
+    return CompilerContext(CompilerContextKind::Namespace, ConstString(name));
+  };
+
+  EXPECT_THAT(
+      (std::vector{make_module("A"), make_module("B"), make_class("C")}),
+      Matches(std::vector{make_module("A"), any_module, make_class("C")}));
+  EXPECT_THAT(
+      (std::vector{make_module("A"), make_class("C")}),
+      Matches(std::vector{make_module("A"), any_module, make_class("C")}));
+  EXPECT_THAT((std::vector{make_module("A"), make_class("C")}),
+              Matches(std::vector{any_module, make_class("C")}));
+  EXPECT_THAT((std::vector{make_module("A"), any_module, make_class("C")}),
+              Not(Matches(std::vector{make_module("A"), make_class("C")})));
+  EXPECT_THAT(
+      (std::vector{make_module("A"), make_module("B"), make_module("C"),
+                   make_class("C")}),
+      Matches(std::vector{make_module("A"), any_module, make_class("C")}));
+  EXPECT_THAT(
+      (std::vector{make_module("A"), make_module("B"), make_module("C"),
+                   make_module("A"), make_module("B"), make_module("C")}),
+      Matches(std::vector{make_module("A"), any_module, make_module("C")}));
+  EXPECT_THAT(
+      (std::vector{make_module("A"), make_module("B"), make_module("C"),
+                   make_module("A"), make_module("B"), make_module("C")}),
+      Matches(std::vector{make_module("A"), any_module, any_module,
+                          make_module("C")}));
+  EXPECT_THAT(
+      (std::vector{make_module("A"), make_module("B"), make_module("C"),
+                   make_module("A"), make_module("B"), make_module("C")}),
+      Matches(std::vector{any_module, make_module("A"), any_module,
+                          make_module("C")}));
+  EXPECT_THAT(
+      (std::vector{make_module("A"), make_module("B"), make_module("C"),
+                   make_module("A"), make_module("B"), make_module("C")}),
+      Matches(std::vector{any_module, make_module("A"), any_module,
+                          make_module("C")}));
+  EXPECT_THAT(
+      (std::vector{make_module("A"), make_module("B"), make_module("C"),
+                   make_module("A"), make_module("B"), make_module("C")}),
+      Not(Matches(std::vector{any_module, make_module("B"), any_module,
+                              make_module("A"), any_module, make_module("A"),
+                              any_module})));
+  EXPECT_THAT((std::vector{make_module("A"), make_module("B"), make_enum("C")}),
+              Matches(std::vector{make_module("A"), make_module("B"),
+                                  make_any_type("C")}));
+  EXPECT_THAT(
+      (std::vector{make_module("A"), make_module("B"), make_class("C")}),
+      Matches(
+          std::vector{make_module("A"), make_module("B"), make_any_type("C")}));
+  EXPECT_THAT(
+      (std::vector{make_module("A"), make_module("B"), make_enum("C2")}),
+      Not(Matches(std::vector{make_module("A"), make_module("B"),
+                              make_any_type("C")})));
+  EXPECT_THAT((std::vector{make_class("C")}),
+              Matches(std::vector{make_class("C")}));
+  EXPECT_THAT((std::vector{make_namespace("NS"), make_class("C")}),
+              Not(Matches(std::vector{make_any_type("C")})));
 }

Copy link
Collaborator

@clayborg clayborg left a comment

Choose a reason for hiding this comment

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

Anytime we change this code it makes me nervous. The unit tests are quite simple and not sure how they match up against real world lookups, but as far as I can tell this looks ok.

Copy link
Member

@Michael137 Michael137 left a comment

Choose a reason for hiding this comment

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

LGTM (left some minor questions)

I've also recently bumped into the issue of FindTypes/FindNamespace/etc. failing to find matches due to presence of (anonymous namespace) entries in the context, so thanks for fixing that

Comment on lines +152 to +154
std::find_if(pat, pat_end, [](const CompilerContext &p) {
return (p.kind & CompilerContextKind::AnyModule) !=
CompilerContextKind::Module;
Copy link
Member

Choose a reason for hiding this comment

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

Couldn't this just be:

Suggested change
std::find_if(pat, pat_end, [](const CompilerContext &p) {
return (p.kind & CompilerContextKind::AnyModule) !=
CompilerContextKind::Module;
std::find_if(pat, pat_end, [](const CompilerContext &p) {
return p.kind != CompilerContextKind::Module;

Since we've already skipped all the AnyModule patterns before we get here?

for (auto pat = m_context.rbegin(), pat_end = m_context.rend();
pat != pat_end;) {

// Handle AnyModule matches. These are tricky as they can match any number
Copy link
Member

Choose a reason for hiding this comment

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

Do we know why AnyModule allows for this wildcard behaviour? I don't see it used anywhere, only exact Module matches

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's a very good question actually. I'm pretty sure we're not using it. I assumed it was being used for some Apple thing with -gmodules or whatever, but now mention that, it doesn't appear to be actually used except for some specialized tests.

So, as much as I loved implementing a glob algorithm, I would love even more to delete it.

Copy link
Member

Choose a reason for hiding this comment

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

Actually just grepped for this in the apple/llvm-project fork and looks like we're using it in the Swift plugin: https://github.com/swiftlang/llvm-project/blob/ee8bc8b8d30eb99807adbcd3c1f044e00af66cdd/lldb/source/Plugins/TypeSystem/Swift/TypeSystemSwiftTypeRef.cpp#L219-L225

@adrian-prantl @augusto2112 @kastiglione could you elaborate on the usage of this lookup-type here and why it's needed? An unfortunate side-effect of only having it be exercised in the Swift plugin is that we need to support this non-trivial matching algorithm while having the tests in a different repo.

That being said, I don't want to block this PR on this question, since the change itself LGTM

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm fine with waiting, as this is a non-trivial piece of code, and if that's the only use case, then we can probably come up with something simpler. For example, some new TypeQuery flag which basically says "exact match except for modules" (that's something we may want to do for anonymous namespaces as well).

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good

Generally, it seems like following two concepts have been conflated in CompilerDeclContextKind: having wildcards in a TypeQuery versus matching two entities regardless of DeclContextKind (e.g., if a union or enum have the same name, then AnyType would allow a match). And as you say, having the wildcard behaviour live separately would make the CompilerDeclContextKind easier to understand again.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hmmm... Ping? Do we still need to wait for opinions from @adrian-prantl @augusto2112 @kastiglione or should I just go ahead and implement the simplified version?

Copy link
Member

@Michael137 Michael137 Jul 26, 2024

Choose a reason for hiding this comment

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

@adrian-prantl was OOO for a couple of weeks, but should be back today. Not sure he'll see this today though (I suspect the review queue might be quite high). I can help run the swift plugin tests if you end up refactoring this. Think it would also be fine to merge this and rework it afterwards. Whatever you prefer

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'll try to create simplified version of the patch (probably tomorrow). I'd appreciate if you could give it a spin.

Copy link
Member

@Michael137 Michael137 left a comment

Choose a reason for hiding this comment

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

Should we disallow certain CompilerContextKind combinations from being used in a query? E.g., AnyModule | Type, etc. shouldn't be something we need to worry about supporting?

@labath
Copy link
Collaborator Author

labath commented Jul 18, 2024

LGTM (left some minor questions)

I've also recently bumped into the issue of FindTypes/FindNamespace/etc. failing to find matches due to presence of (anonymous namespace) entries in the context, so thanks for fixing that

We're not there yet. :)

Copy link
Collaborator

@clayborg clayborg left a comment

Choose a reason for hiding this comment

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

My main questions is do we actually use wildcards a lot? In normal type query matches, we can ask for exact matches where everything is specified, or the user types some string like a::b::iterator where we don't know what any of the items are. If exact match is specified then all levels need to match the type we found, but if exact match is not specified, then we accept anything that ends with the specified compiler context. There are no wildcards specified in any of these kinds of queries from a type lookup perspective right? Where are these actual wildcard searches happening?

When searching for a type in anonymous namespaces, are you expecting the compiler context that gets created for such types is doing to include the anonymous namespace in the context? So for:

namespace {
  struct Foo {};
}

The compiler context will end up being:

namespace ""
struct "Foo"

We might need to notion of a declaration context barrier item in our CompilerContext arrays that say "the type below this is fully qualified on its own, but this barrier can help disambiguate searches if the user fully specifies things. For example a type in a function right now:

namespace a {
void func1() {
  namespace b {
   struct foo{};
 };
}

Currently will produce a compiler contenxt of:

namespace "b"
struct "foo"

But could it could include "func1"'s context in the future:

namespace "a"
function "func1" <<<< barrier entry (for lack of a better term)
namespace "b"
struct "foo"

Then a search for an exact type name "b::foo" could match the above compiler context as it matches fully up to a barrier CompilerContext. But if the users knows they want this exact type they could ask for "a::func1::b::foo" and get the right one if they were other "b::foo" types.

I mention this because for anonynmous namespace types, we might not want to have to type "(anonymous namespace)::Foo" to find the type from my first example, users probably just want to type "Foo" and still find the type and the anonymous namespace could be one of these new decl context root barriers.

Any clarification on what you plan to do for anonymous types would be great to hear.

@labath
Copy link
Collaborator Author

labath commented Jul 29, 2024

My idea for anonymous namespaces was basically to treat them as optional, so that you could get a match (even an "exact" match maybe?) for (anonymous namespace)::Foo with a pattern Foo. However, if a pattern explicitly contained an (anonymous namespace), then this would have to be present in the type as well (*).

The reason I'm looking into this is because we got a bug report that dynamic type resolution isn't working. This works by demangling the type vtable, and then looking up the type with the demangled name. This doesn't didn't work for types in anonymous namespaces.

Now that you mention functions, I see that this also wouldn't work for types defined in functions. However, that isn't a bridge I want to cross right now.

(*) One of the annoying things about anonymous namespaces is that they can appear anywhere, and even multiple times, so you can end up with types like A::(anonymous namespace)::B::(anonymous namespace)::T. This should work with my patch.

labath added a commit to labath/llvm-project that referenced this pull request Jul 31, 2024
This is an alternative, much simpler implementation of llvm#99305. In this
version I replace the AnyModule wildcard match with a special TypeQuery
flag which achieves (mostly) the same thing.

It is a preparatory step for teaching ContextMatches about anonymous
namespaces. It started out as a way to remove the assumption that the
pattern and target contexts must be of the same length -- that's will
not be correct with anonymous namespaces, and probably isn't even
correct right now for AnyModule matches.
labath added a commit that referenced this pull request Aug 5, 2024
This is an alternative, much simpler implementation of #99305. In this
version I replace the AnyModule wildcard match with a special TypeQuery
flag which achieves (mostly) the same thing.

It is a preparatory step for teaching ContextMatches about anonymous
namespaces. It started out as a way to remove the assumption that the
pattern and target contexts must be of the same length -- that's will
not be correct with anonymous namespaces, and probably isn't even
correct right now for AnyModule matches.
@labath
Copy link
Collaborator Author

labath commented Aug 5, 2024

Superseeded by #101333

@labath labath closed this Aug 5, 2024
@labath labath deleted the lookup branch November 8, 2024 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants