Skip to content
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

[clang] Fix a bug with qualified name lookup into current instantiation #73018

Merged
merged 4 commits into from
Nov 30, 2023

Conversation

Fznamznon
Copy link
Contributor

Due to d0d2ee0 clang doesn't perform qualified name lookup into the current instantiation when it has dependent bases, because of that getTypeName call always returns null for unknown specialization case. When there is a typename keyword, DependentNameType is constructed instead of simply returning null.
This change attempts to do the same in case of typename absence.

Fixes #13826

Due to d0d2ee0 clang doesn't perform qualified
name lookup into the current instantiation when it has dependent bases, because
of that `getTypeName` call always returns null for unknown specialization case.
When there is a `typename` keyword, `DependentNameType` is constructed instead
of simply returning null.
This change attempts to do the same in case of `typename` absence.

Fixes llvm#13826
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Nov 21, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Nov 21, 2023

@llvm/pr-subscribers-clang

Author: Mariya Podchishchaeva (Fznamznon)

Changes

Due to d0d2ee0 clang doesn't perform qualified name lookup into the current instantiation when it has dependent bases, because of that getTypeName call always returns null for unknown specialization case. When there is a typename keyword, DependentNameType is constructed instead of simply returning null.
This change attempts to do the same in case of typename absence.

Fixes #13826


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

3 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+3)
  • (modified) clang/lib/Sema/SemaDecl.cpp (+17-3)
  • (modified) clang/test/SemaTemplate/dependent-base-classes.cpp (+14)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 157afd9e8629152..09ceb591d06eab5 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -610,6 +610,9 @@ Bug Fixes in This Version
   inside a lambda. (`#61460 <https://github.com/llvm/llvm-project/issues/61460>`_)
 - Fix crash during instantiation of some class template specializations within class
   templates. Fixes (`#70375 <https://github.com/llvm/llvm-project/issues/70375>`_)
+- Fixed false positive error emitted by clang when performing qualified name
+  lookup and the current class instantiation has dependent bases.
+  Fixes (`#13826 <https://github.com/llvm/llvm-project/issues/13826>`_)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 4e1857b931cc868..71003c1815c90e4 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -442,7 +442,6 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
   UsingShadowDecl *FoundUsingShadow = nullptr;
   switch (Result.getResultKind()) {
   case LookupResult::NotFound:
-  case LookupResult::NotFoundInCurrentInstantiation:
     if (CorrectedII) {
       TypeNameValidatorCCC CCC(/*AllowInvalid=*/true, isClassName,
                                AllowDeducedTemplate);
@@ -482,8 +481,23 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
         }
       }
     }
-    // If typo correction failed or was not performed, fall through
-    [[fallthrough]];
+    Result.suppressDiagnostics();
+    return nullptr;
+  case LookupResult::NotFoundInCurrentInstantiation: {
+    if (AllowImplicitTypename == ImplicitTypenameContext::Yes) {
+      QualType T;
+      T = Context.getDependentNameType(ElaboratedTypeKeyword::None,
+                                       SS->getScopeRep(), &II);
+      TypeLocBuilder TLB;
+      DependentNameTypeLoc TL = TLB.push<DependentNameTypeLoc>(T);
+      TL.setElaboratedKeywordLoc(SourceLocation());
+      TL.setQualifierLoc(SS->getWithLocInContext(Context));
+      TL.setNameLoc(NameLoc);
+      return CreateParsedType(T, TLB.getTypeSourceInfo(Context, T));
+    }
+    Result.suppressDiagnostics();
+    return nullptr;
+  }
   case LookupResult::FoundOverloaded:
   case LookupResult::FoundUnresolvedValue:
     Result.suppressDiagnostics();
diff --git a/clang/test/SemaTemplate/dependent-base-classes.cpp b/clang/test/SemaTemplate/dependent-base-classes.cpp
index 09f475f8bde9183..92a37efaa7e73f6 100644
--- a/clang/test/SemaTemplate/dependent-base-classes.cpp
+++ b/clang/test/SemaTemplate/dependent-base-classes.cpp
@@ -130,3 +130,17 @@ namespace PR5812 {
 
   Derived<int> di;
 }
+
+namespace GH13826 {
+template <typename T> struct A {
+  typedef int type;
+  struct B;
+};
+
+template <typename T> struct A<T>::B : A<T> {
+  B::type t;
+};
+
+A<int> a;
+A<int>::B b;
+}

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

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

The patch makes sense to me, though my knowledge of this function is limited. Please give @cor3ntin a chance to look at it too in case he sees something I don't.

clang/lib/Sema/SemaDecl.cpp Outdated Show resolved Hide resolved
clang/lib/Sema/SemaDecl.cpp Outdated Show resolved Hide resolved
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.

That looks reasonable to me modulo nit and @shafik comments.
Thanks for fixing this!

SS->getScopeRep(), &II);
TypeLocBuilder TLB;
DependentNameTypeLoc TL = TLB.push<DependentNameTypeLoc>(T);
TL.setElaboratedKeywordLoc(SourceLocation());
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think that's actually needed even if we do it in a bunch of places.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, we probably need this. Sanitizer build failed due to elaborated keyword loc not initialized https://lab.llvm.org/buildbot/#/builders/5/builds/38796/steps/2/logs/stdio .

@Fznamznon Fznamznon merged commit 14ca8d4 into llvm:main Nov 30, 2023
4 checks passed
@Fznamznon
Copy link
Contributor Author

Fix for buildbot #73928
I'm not able to verify locally though

@john-brawn-arm
Copy link
Collaborator

As a result of this commit clang no longer gives an error for

template <int A> struct X {
  enum E { a };
};

template <int A> struct Y : X<A> {
  Y::E m;
};

where gcc (though not msvc) gives an error: https://godbolt.org/z/qGfnzhfsK

@john-brawn-arm
Copy link
Collaborator

john-brawn-arm commented Dec 1, 2023

I'm not completely sure if we should be giving an error for the example I gave above. Looking through the C++20 standard I see temp.res paragraph 2:

A name used in a template declaration or definition and that is dependent on a template-parameter is assumed
not to name a type unless the applicable name lookup finds a type name or the name is qualified by the
keyword typename

The error that gcc is giving is

<source>:6:6: error: invalid use of incomplete type 'struct Y<A>'
    6 |   Y::E m;
      |      ^
<source>:5:25: note: definition of 'struct Y<A>' is not complete until the closing brace
    5 | template <int A> struct Y : X<A> {
      |                         ^

so I'm guessing that gcc thinks that the name lookup for Y::E doesn't find a type name because Y is incomplete, though I don't understand why the same isn't true for the example in #13826

template <typename T> struct A {
  typedef int type;
  struct B;
};

template <typename T> struct A<T>::B : A<T> {
  B::type t;
};

Maybe the rule in temp.res doesn't apply because this is a member of the template and not the template itself?

@Fznamznon
Copy link
Contributor Author

I'm not sure I understand why gcc is giving an error in https://godbolt.org/z/qGfnzhfsK. It doesn't give an error with typename keyword and I think there shouldn't be any difference since Y::E refers to current instantiation and is being used by class member declaration so no typename required.
cc @shafik @cor3ntin @zygoloid for an opinion.

@perry-ca
Copy link
Contributor

Was this PR meant to change the behaviour for c++11/14?

@zygoloid
Copy link
Collaborator

I'm not sure I understand why gcc is giving an error in https://godbolt.org/z/qGfnzhfsK. It doesn't give an error with typename keyword and I think there shouldn't be any difference since Y::E refers to current instantiation and is being used by class member declaration so no typename required. cc @shafik @cor3ntin @zygoloid for an opinion.

This looks like a GCC bug to me. I think the presence or absence of typename isn't supposed to make a difference in an implicit typename context.

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.

Ignores dependent base classes using qualified lookup in definition context
8 participants