Skip to content

[Clang] Fix name lookup for dependent bases #114978

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 1 commit into from
Nov 26, 2024

Conversation

vbe-sc
Copy link
Contributor

@vbe-sc vbe-sc commented Nov 5, 2024

Currently the following example is a compilation failure:

template<typename T> struct A {
    typedef int M;
    struct B {
      typedef void M;
      struct C;
    };
};

template<typename T> struct A<T>::B::C : A<T> {
    M m; // void or int ?
};

According to the point 13.8.3.2

A dependent base class is a base class that is a dependent type and is not the current instantiation.
Note 2 : A base class can be the current instantiation in the case of a nested class naming an enclosing class as a base.

The base class A is the current instantiation, because C is a nested class for an enclosing class A<T>, it's is the not-dependent base class and we need to search the names through its scope.

This patch makes this example compile

@vbe-sc vbe-sc requested a review from Endilll as a code owner November 5, 2024 12:48
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Nov 5, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 5, 2024

@llvm/pr-subscribers-clang

Author: Vladislav Belov (vbe-sc)

Changes

Currently the following example is a compilation failure:

template&lt;typename T&gt; struct A {
    typedef int M;
    struct B {
      typedef void M;
      struct C;
    };
};

template&lt;typename T&gt; struct A&lt;T&gt;::B::C : A&lt;T&gt; {
    M m; // void or int ?
};

According to the point 13.8.3.2

A dependent base class is a base class that is a dependent type and is not the current instantiation.
Note 2 : A base class can be the current instantiation in the case of a nested class naming an enclosing class as a base.

The base class A is the current instantiation, because C is a nested class for an enclosing class A&lt;T&gt;, it's is the not-dependent base class and we need to search the names through its scope.

This patch makes this example compile


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

2 Files Affected:

  • (modified) clang/lib/AST/CXXInheritance.cpp (+8-5)
  • (modified) clang/test/CXX/drs/cwg5xx.cpp (+6-2)
diff --git a/clang/lib/AST/CXXInheritance.cpp b/clang/lib/AST/CXXInheritance.cpp
index eb265a872c1259..049532f942d051 100644
--- a/clang/lib/AST/CXXInheritance.cpp
+++ b/clang/lib/AST/CXXInheritance.cpp
@@ -169,6 +169,9 @@ bool CXXBasePaths::lookupInBases(ASTContext &Context,
     // Find the record of the base class subobjects for this type.
     QualType BaseType =
         Context.getCanonicalType(BaseSpec.getType()).getUnqualifiedType();
+    bool isCurrentInstantiation = false;
+    if (auto *TST = BaseSpec.getType()->getAs<TemplateSpecializationType>())
+      isCurrentInstantiation = TST->isCurrentInstantiation();
 
     // C++ [temp.dep]p3:
     //   In the definition of a class template or a member of a class template,
@@ -176,7 +179,8 @@ bool CXXBasePaths::lookupInBases(ASTContext &Context,
     //   the base class scope is not examined during unqualified name lookup
     //   either at the point of definition of the class template or member or
     //   during an instantiation of the class tem- plate or member.
-    if (!LookupInDependent && BaseType->isDependentType())
+    if (!LookupInDependent &&
+        (BaseType->isDependentType() && !isCurrentInstantiation))
       continue;
 
     // Determine whether we need to visit this base class at all,
@@ -244,9 +248,8 @@ bool CXXBasePaths::lookupInBases(ASTContext &Context,
         return FoundPath;
       }
     } else if (VisitBase) {
-      CXXRecordDecl *BaseRecord;
+      CXXRecordDecl *BaseRecord = nullptr;
       if (LookupInDependent) {
-        BaseRecord = nullptr;
         const TemplateSpecializationType *TST =
             BaseSpec.getType()->getAs<TemplateSpecializationType>();
         if (!TST) {
@@ -265,8 +268,8 @@ bool CXXBasePaths::lookupInBases(ASTContext &Context,
             BaseRecord = nullptr;
         }
       } else {
-        BaseRecord = cast<CXXRecordDecl>(
-            BaseSpec.getType()->castAs<RecordType>()->getDecl());
+        if (auto *RT = BaseSpec.getType()->getAs<RecordType>())
+          BaseRecord = cast<CXXRecordDecl>(RT->getDecl());
       }
       if (BaseRecord &&
           lookupInBases(Context, BaseRecord, BaseMatches, LookupInDependent)) {
diff --git a/clang/test/CXX/drs/cwg5xx.cpp b/clang/test/CXX/drs/cwg5xx.cpp
index ed0c7159dfc889..b283684aef2f7e 100644
--- a/clang/test/CXX/drs/cwg5xx.cpp
+++ b/clang/test/CXX/drs/cwg5xx.cpp
@@ -1178,17 +1178,21 @@ namespace cwg590 { // cwg590: yes
   template<typename T> typename A<T>::B::C A<T>::B::C::f(A<T>::B::C) {}
 }
 
-namespace cwg591 { // cwg591: no
+namespace cwg591 { // cwg591: yes
   template<typename T> struct A {
     typedef int M;
     struct B {
       typedef void M;
       struct C;
+      struct D;
     };
   };
 
   template<typename T> struct A<T>::B::C : A<T> {
-    // FIXME: Should find member of non-dependent base class A<T>.
+    M m;
+  };
+
+  template<typename T> struct A<T>::B::D : A<T*> {
     M m;
     // expected-error@-1 {{field has incomplete type 'M' (aka 'void'}}
   };

@Endilll Endilll added the c++ label Nov 5, 2024
@vbe-sc vbe-sc force-pushed the vb-sc/clang-dependent-lookup-fix branch from 4eb7be6 to 0c2363c Compare November 5, 2024 14:12
@vbe-sc
Copy link
Contributor Author

vbe-sc commented Nov 6, 2024

@Endilll, please, could you take a look?
Also, do you have any ideas why the Win pipeline fails with checkout failure? How can I fix it?

@Endilll
Copy link
Contributor

Endilll commented Nov 6, 2024

Also, do you have any ideas why the Win pipeline fails with checkout failure? How can I fix it?

It seems to be unrelated to you. Might be fixed when you'll push a new commit.

@Endilll
Copy link
Contributor

Endilll commented Nov 6, 2024

@Endilll, please, could you take a look?

My knowledge of wording about templates is not deep enough, unfortunately.

@vbe-sc
Copy link
Contributor Author

vbe-sc commented Nov 6, 2024

@Endilll, please, could you take a look?

My knowledge of wording about templates is not deep enough, unfortunately.

Could you, please, suggest someone who can review this part?

@Endilll
Copy link
Contributor

Endilll commented Nov 6, 2024

@Endilll, please, could you take a look?

My knowledge of wording about templates is not deep enough, unfortunately.

Could you, please, suggest someone who can review this part?

I've already added several people who should be able to, but mind that we're very short on reviewers, so this will likely take time.

@vbe-sc vbe-sc force-pushed the vb-sc/clang-dependent-lookup-fix branch from 0c2363c to 66f3465 Compare November 7, 2024 09:07
@vbe-sc
Copy link
Contributor Author

vbe-sc commented Nov 8, 2024

@sdkrystian, please, could you take a look?

@vbe-sc vbe-sc requested a review from mizvekov November 8, 2024 15:22
Copy link
Contributor

@mizvekov mizvekov left a comment

Choose a reason for hiding this comment

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

LGTM, but give some time for @sdkrystian to take a look, since he is actively working on this area.

@vbe-sc
Copy link
Contributor Author

vbe-sc commented Nov 10, 2024

@sdkrystian, could you please take a look?

@sdkrystian
Copy link
Member

@vbe-sc I can later today or tomorrow.

@vbe-sc
Copy link
Contributor Author

vbe-sc commented Nov 11, 2024

@vbe-sc I can later today or tomorrow.

That's would be great, thank you!

@vbe-sc
Copy link
Contributor Author

vbe-sc commented Nov 12, 2024

@sdkrystian ping

@vbe-sc
Copy link
Contributor Author

vbe-sc commented Nov 14, 2024

@sdkrystian, could you, please, take a look? We are about to merge this

@mizvekov
Copy link
Contributor

Please wait for a week before pinging someone again.

I think this is fine to merge as is, if you want to go ahead. We can always do post commit review later.

@vbe-sc
Copy link
Contributor Author

vbe-sc commented Nov 15, 2024

Please wait for a week before pinging someone again.

I think this is fine to merge as is, if you want to go ahead. We can always do post commit review later.

Sorry, I didn't know.
Let's do it this way you want

Copy link
Member

@sdkrystian sdkrystian left a comment

Choose a reason for hiding this comment

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

I don't think this patch fixes the following case:

template<typename T> 
struct A 
{
    struct B 
    {
        using X = int;

        struct C
        {
            using X = void;

            struct D;
        };
    };
};

template<typename T> 
struct A<T>::B::C::D : B
{
    X x; // error: field has incomplete type 'X' (aka 'void')
};

Regardless, this patch should probably include this as a test.

@vbe-sc vbe-sc force-pushed the vb-sc/clang-dependent-lookup-fix branch from 66f3465 to 0de2ff2 Compare November 17, 2024 01:41
@vbe-sc
Copy link
Contributor Author

vbe-sc commented Nov 17, 2024

I don't think this patch fixes the following case:

template<typename T> 
struct A 
{
    struct B 
    {
        using X = int;

        struct C
        {
            using X = void;

            struct D;
        };
    };
};

template<typename T> 
struct A<T>::B::C::D : B
{
    X x; // error: field has incomplete type 'X' (aka 'void')
};

Regardless, this patch should probably include this as a test.

Many thanks for your answer! You're right, this test failed with this patch, but I've already fixed it (see the new changes).
Moreover, I have added more tests that should and shouldn't compile.

@vbe-sc vbe-sc force-pushed the vb-sc/clang-dependent-lookup-fix branch 2 times, most recently from 2cea79d to f3b8ee9 Compare November 17, 2024 03:30
@vbe-sc vbe-sc force-pushed the vb-sc/clang-dependent-lookup-fix branch from f3b8ee9 to 5a48960 Compare November 17, 2024 03:39
@vbe-sc vbe-sc requested a review from sdkrystian November 17, 2024 13:38
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.

Can you add a release note and update cxx_dr_status?
Thanks

@@ -170,13 +170,21 @@ bool CXXBasePaths::lookupInBases(ASTContext &Context,
QualType BaseType =
Context.getCanonicalType(BaseSpec.getType()).getUnqualifiedType();

bool isCurrentInstantiation = isa<InjectedClassNameType>(BaseType);
if (!isCurrentInstantiation) {
if (auto *BaseRecord = cast_or_null<CXXRecordDecl>(
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (auto *BaseRecord = cast_or_null<CXXRecordDecl>(
if (auto *BaseRecord = cast_if_present<CXXRecordDecl>(

Can this actually be null?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, you are right. Fixed here and in the other part of the code because there is the same case.

@vbe-sc vbe-sc force-pushed the vb-sc/clang-dependent-lookup-fix branch 2 times, most recently from 2f7c763 to e2e4907 Compare November 18, 2024 14:45
@vbe-sc vbe-sc requested a review from cor3ntin November 18, 2024 15:22
@vbe-sc vbe-sc force-pushed the vb-sc/clang-dependent-lookup-fix branch from e2e4907 to d17588a Compare November 19, 2024 09:50
@vbe-sc
Copy link
Contributor Author

vbe-sc commented Nov 19, 2024

Can you add a release note

I added a release note as you asked

@vbe-sc
Copy link
Contributor Author

vbe-sc commented Nov 21, 2024

@sdkrystian, can you, please, re-review this patch? It seems like I fixed your test

@vbe-sc
Copy link
Contributor Author

vbe-sc commented Nov 25, 2024

@cor3ntin could you, please, re-review this patch and resolve the comments if everything is ok?

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 (modulo small wording nit in the release note)

@@ -268,6 +268,9 @@ Resolutions to C++ Defect Reports
by default.
(`CWG2521: User-defined literals and reserved identifiers <https://cplusplus.github.io/CWG/issues/2521.html>`_).

- Clang now make correct name lookup when dependent base class is the current instantiation.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- Clang now make correct name lookup when dependent base class is the current instantiation.
- Fix name lookup for a dependent base class that is the current instantiation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed, thanks

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could we merge this?

@vbe-sc vbe-sc force-pushed the vb-sc/clang-dependent-lookup-fix branch from d17588a to 6564e20 Compare November 26, 2024 09:00
@cor3ntin cor3ntin merged commit 4866447 into llvm:main Nov 26, 2024
9 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Nov 26, 2024

LLVM Buildbot has detected a new failure on builder clangd-ubuntu-tsan running on clangd-ubuntu-clang while building clang at step 6 "test-build-clangd-clangd-index-server-clangd-in...".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/134/builds/9282

Here is the relevant piece of the build log for the reference
Step 6 (test-build-clangd-clangd-index-server-clangd-in...) failure: test (failure)
******************** TEST 'Clangd :: target_info.test' FAILED ********************
Exit Code: 66

Command Output (stderr):
--
RUN: at line 5: rm -rf /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.dir && mkdir -p /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.dir
+ rm -rf /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.dir
+ mkdir -p /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.dir
RUN: at line 7: echo '[{"directory": "/vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.dir", "command": "/vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.dir/armv7-clang -x c++ the-file.cpp -v", "file": "the-file.cpp"}]' > /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.dir/compile_commands.json
+ echo '[{"directory": "/vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.dir", "command": "/vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.dir/armv7-clang -x c++ the-file.cpp -v", "file": "the-file.cpp"}]'
RUN: at line 9: sed -e "s|INPUT_DIR|/vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.dir|g" /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/llvm-project/clang-tools-extra/clangd/test/target_info.test > /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.test.1
+ sed -e 's|INPUT_DIR|/vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.dir|g' /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/llvm-project/clang-tools-extra/clangd/test/target_info.test
RUN: at line 12: sed -E -e 's|"file://([A-Z]):/|"file:///\1:/|g' /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.test.1 > /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.test
+ sed -E -e 's|"file://([A-Z]):/|"file:///\1:/|g' /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.test.1
RUN: at line 14: clangd -lit-test < /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.test 2>&1 | /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/bin/FileCheck -strict-whitespace /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.test
+ clangd -lit-test
+ /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/bin/FileCheck -strict-whitespace /vol/worker/clangd-ubuntu-clang/clangd-ubuntu-tsan/build/tools/clang/tools/extra/clangd/test/Output/target_info.test.tmp.test

--

********************


asi-sc added a commit to asi-sc/llvm-project that referenced this pull request Nov 26, 2024
@asi-sc
Copy link
Contributor

asi-sc commented Nov 26, 2024

@vbe-sc , reverted as you asked me offline #117727 . Feel free to create a new PR which includes the original commit and the fix for problem.

@nikic
Copy link
Contributor

nikic commented Nov 26, 2024

FYI this seems to have significant cost for some types of C++ code, in particular clang build time regresses by 0.35% (https://llvm-compile-time-tracker.com/compare.php?from=4a7b56e6e7dd0f83c379ad06b6e81450bc691ba6&to=486644723038555a224fd09d462bb5099e64809e&stat=instructions:u).

@erichkeane
Copy link
Collaborator

Hmm, that compile time regression is unfortunate. @vbe-sc as a part of your next version of this patch, can you do some sort of analysis why this would result in further lookups/instantiation/etc? I could comprehend that perhaps we're skipping the 'current instantiation' now for these bases so now there is more work to do in instantiations (of which there are more of those than the base, sort of out of necessity), but would like some sort of analysis/confirmation that is the case/what is happening in some of those 'worst' comparisons.

@vbe-sc
Copy link
Contributor Author

vbe-sc commented Nov 27, 2024

Such an example causes a failure in my patch

template <class T> 
class X {
public:
  X() = default;
  virtual ~X() = default;

  virtual int foo(int x, int y, T &entry) = 0;

  void bar() {
    struct Y : public X<T> {
      Y() : X() {}

      int foo(int, int, T &) override {
        return 42;
      }
    };
  }
};

The output

llvm-project/clang/lib/AST/DeclCXX.cpp:2508: void clang::CXXMethodDecl::addOverriddenMethod(const CXXMethodDecl *): Assertion `!MD->getParent()->isDependentContext() && "Can't add an overridden method to a class template!"' failed.

The problem case is class definition with overrided metod.
I'm working on investigation and fixing this issue

@vbe-sc
Copy link
Contributor Author

vbe-sc commented Nov 27, 2024

void CXXMethodDecl::addOverriddenMethod(const CXXMethodDecl *MD) {
  assert(MD->isCanonicalDecl() && "Method is not canonical!");
  assert(!MD->getParent()->isDependentContext() &&
         "Can't add an overridden method to a class template!");

It seems to me that the second assertion is not really a requirement. @mizvekov , can you please comment on this statement?

asi-sc pushed a commit that referenced this pull request Dec 3, 2024
Unlike the previous version
(#114978), this patch also
removes an unnecessary assert that causes Clang to crash when compiling
such tests. (clang/lib/AST/DeclCXX.cpp)

https://lab.llvm.org/buildbot/#/builders/52/builds/4021

```c++
template <class T> 
class X {
public:
  X() = default;
  virtual ~X() = default;

  virtual int foo(int x, int y, T &entry) = 0;

  void bar() {
    struct Y : public X<T> {
      Y() : X() {}

      int foo(int, int, T &) override {
        return 42;
      }
    };
  }
};
```

the assertions: 

```c++
llvm-project/clang/lib/AST/DeclCXX.cpp:2508: void clang::CXXMethodDecl::addOverriddenMethod(const CXXMethodDecl *): Assertion `!MD->getParent()->isDependentContext() && "Can't add an overridden method to a class template!"' failed.
```

I believe that this assert is unnecessary and contradicts the logic of
this patch. After its removal, Clang was successfully built using
itself, and all tests passed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ 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.

10 participants