Skip to content

[AST] RecursiveASTVisitor: Don't traverse the alias deduction guides in the default mode. #91454

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

Merged
merged 2 commits into from
May 16, 2024

Conversation

hokein
Copy link
Collaborator

@hokein hokein commented May 8, 2024

By default (shouldVisitImplicitCode() returns false), RAV should not traverse AST nodes that are not spelled in the source code. Deduction guides for alias templates are always synthesized, so they should not be traversed.

This is usually done by checking the implicit bit of the Decl. However, this doesn't work deduction guides that are synthesized from explicit user-defined deduction guides, as we must maintain the explicit bit to ensure correct overload resolution.

@hokein hokein requested a review from cor3ntin May 8, 2024 10:27
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels May 8, 2024
@llvmbot
Copy link
Member

llvmbot commented May 8, 2024

@llvm/pr-subscribers-clang

Author: Haojian Wu (hokein)

Changes

By default (shouldVisitImplicitCode() returns false), RAV should not traverse AST nodes that are not spelled in the source code. Deduction guides for alias templates are always synthesized, so they should not be traversed.

This is usually done by checking the implicit bit of the Decl. However, this doesn't work deduction guides that are synthesized from explicit user-defined deduction guides, as we must maintain the explicit bit to ensure correct overload resolution.


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

2 Files Affected:

  • (modified) clang/include/clang/AST/RecursiveASTVisitor.h (+21-7)
  • (added) clang/unittests/Tooling/RecursiveASTVisitorTests/DeductionGuide.cpp (+89)
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index f9b145b4e86a5..2517189c95300 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -736,13 +736,27 @@ bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) {
 
   // As a syntax visitor, by default we want to ignore declarations for
   // implicit declarations (ones not typed explicitly by the user).
-  if (!getDerived().shouldVisitImplicitCode() && D->isImplicit()) {
-    // For an implicit template type parameter, its type constraints are not
-    // implicit and are not represented anywhere else. We still need to visit
-    // them.
-    if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(D))
-      return TraverseTemplateTypeParamDeclConstraints(TTPD);
-    return true;
+  if (!getDerived().shouldVisitImplicitCode()) {
+    if (D->isImplicit()) {
+      // For an implicit template type parameter, its type constraints are not
+      // implicit and are not represented anywhere else. We still need to visit
+      // them.
+      if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(D))
+        return TraverseTemplateTypeParamDeclConstraints(TTPD);
+      return true;
+    }
+
+    // Deduction guides for alias templates are always synthesized, so they
+    // should not be traversed unless shouldVisitImplicitCode() returns true.
+    //
+    // It's important to note that checking the implicit bit is not efficient
+    // for the alias case. For deduction guides synthesized from explicit
+    // user-defined deduction guides, we must maintain the explicit bit to
+    // ensure correct overload resolution.
+    if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
+      if (llvm::isa_and_present<TypeAliasTemplateDecl>(
+              FTD->getDeclName().getCXXDeductionGuideTemplate()))
+        return true;
   }
 
   switch (D->getKind()) {
diff --git a/clang/unittests/Tooling/RecursiveASTVisitorTests/DeductionGuide.cpp b/clang/unittests/Tooling/RecursiveASTVisitorTests/DeductionGuide.cpp
new file mode 100644
index 0000000000000..abfdbaea4a615
--- /dev/null
+++ b/clang/unittests/Tooling/RecursiveASTVisitorTests/DeductionGuide.cpp
@@ -0,0 +1,89 @@
+//===- unittest/Tooling/RecursiveASTVisitorTests/DeductionGuide.cpp -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TestVisitor.h"
+#include <string>
+
+using namespace clang;
+
+namespace {
+
+class DeductionGuideVisitor
+    : public ExpectedLocationVisitor<DeductionGuideVisitor> {
+public:
+  DeductionGuideVisitor(bool ShouldVisitImplicitCode)
+      : ShouldVisitImplicitCode(ShouldVisitImplicitCode) {}
+  bool VisitCXXDeductionGuideDecl(CXXDeductionGuideDecl *D) {
+    std::string Storage;
+    llvm::raw_string_ostream Stream(Storage);
+    D->print(Stream);
+    Match(Stream.str(),D->getLocation());
+    return true;
+  }
+
+  bool shouldVisitTemplateInstantiations() const {
+    return false;
+  }
+
+  bool shouldVisitImplicitCode() const {
+    return ShouldVisitImplicitCode;
+  }
+  bool ShouldVisitImplicitCode;
+};
+
+TEST(RecursiveASTVisitor, DeductionGuideNonImplicitMode) {
+  DeductionGuideVisitor Visitor(/*ShouldVisitImplicitCode*/ false);
+  // Verify that the synthezied deduction guide for alias is not visited in
+  // RAV's implicit mode.
+  Visitor.ExpectMatch("Foo(T) -> Foo<int>", 11, 1);
+  Visitor.DisallowMatch("Bar(type-parameter-0-0) -> Foo<int>", 14, 1);
+  EXPECT_TRUE(Visitor.runOver(
+    R"cpp(
+template <typename T>
+concept False = true;
+
+template <typename T> 
+struct Foo { 
+  Foo(T);
+};
+
+template<typename T> requires False<T>
+Foo(T) -> Foo<int>;
+
+template <typename U>
+using Bar = Foo<U>;
+Bar s(1); 
+   )cpp"
+  , DeductionGuideVisitor::Lang_CXX2a));
+}
+
+TEST(RecursiveASTVisitor, DeductionGuideImplicitMode) {
+  DeductionGuideVisitor Visitor(/*ShouldVisitImplicitCode*/ true);
+  Visitor.ExpectMatch("Foo(T) -> Foo<int>", 11, 1);
+  Visitor.ExpectMatch("Bar(type-parameter-0-0) -> Foo<int>", 14, 1);
+  EXPECT_TRUE(Visitor.runOver(
+    R"cpp(
+template <typename T>
+concept False = true;
+
+template <typename T> 
+struct Foo { 
+  Foo(T);
+};
+
+template<typename T> requires False<T>
+Foo(T) -> Foo<int>;
+
+template <typename U>
+using Bar = Foo<U>;
+Bar s(1); 
+   )cpp"
+  , DeductionGuideVisitor::Lang_CXX2a));
+}
+
+} // end anonymous namespace

Copy link

github-actions bot commented May 8, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

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

LGTM

@hokein hokein merged commit 239f8b9 into llvm:main May 16, 2024
3 of 4 checks passed
@hokein hokein deleted the rav-ctad branch May 16, 2024 10:44
@keith
Copy link
Member

keith commented Jul 22, 2024

@hokein looks like this test wasn't added to any CMakeLists.txt so it doesn't actually run?

@keith
Copy link
Member

keith commented Jul 22, 2024

I think you want it here?

RecursiveASTVisitorTests/DeclRefExpr.cpp

@keith
Copy link
Member

keith commented Jul 22, 2024

I think the tests broke in #99840

@keith
Copy link
Member

keith commented Jul 22, 2024

#99907

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants