[clang] [Clang][Sema] Use the correct lookup context when building overloaded 'operator->' in the current instantiation (PR #104458)

Krystian Stasiowski via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 9 08:33:07 PDT 2024


https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/104458

>From 11f67c73a8de04ce94dbed467de043668234e202 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Thu, 15 Aug 2024 11:27:34 -0400
Subject: [PATCH 1/4] [Clang][Sema] Use the correct lookup context when
 building overloaded 'operator->' in the current instantiation

---
 clang/include/clang/Sema/Sema.h               |  3 +--
 clang/lib/Sema/SemaExprCXX.cpp                | 25 ++++++++-----------
 clang/lib/Sema/SemaOverload.cpp               | 14 +++++++----
 clang/lib/Sema/TreeTransform.h                |  4 +--
 .../temp.res/temp.dep/temp.dep.type/p4.cpp    | 24 ++++++++++--------
 5 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 68c782a15c6f1b..0666640584f53a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10639,8 +10639,7 @@ class Sema final : public SemaBase {
   /// BuildOverloadedArrowExpr - Build a call to an overloaded @c operator->
   ///  (if one exists), where @c Base is an expression of class type and
   /// @c Member is the name of the member we're trying to find.
-  ExprResult BuildOverloadedArrowExpr(Scope *S, Expr *Base,
-                                      SourceLocation OpLoc,
+  ExprResult BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc,
                                       bool *NoArrowOperatorFound = nullptr);
 
   ExprResult BuildCXXMemberCallExpr(Expr *Exp, NamedDecl *FoundDecl,
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 14feafd1e6b17f..f6e09f62cd3782 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -7966,18 +7966,6 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
 
   QualType BaseType = Base->getType();
   MayBePseudoDestructor = false;
-  if (BaseType->isDependentType()) {
-    // If we have a pointer to a dependent type and are using the -> operator,
-    // the object type is the type that the pointer points to. We might still
-    // have enough information about that type to do something useful.
-    if (OpKind == tok::arrow)
-      if (const PointerType *Ptr = BaseType->getAs<PointerType>())
-        BaseType = Ptr->getPointeeType();
-
-    ObjectType = ParsedType::make(BaseType);
-    MayBePseudoDestructor = true;
-    return Base;
-  }
 
   // C++ [over.match.oper]p8:
   //   [...] When operator->returns, the operator-> is applied  to the value
@@ -7992,7 +7980,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
     SmallVector<FunctionDecl*, 8> OperatorArrows;
     CTypes.insert(Context.getCanonicalType(BaseType));
 
-    while (BaseType->isRecordType()) {
+    while (BaseType->getAsRecordDecl()) {
       if (OperatorArrows.size() >= getLangOpts().ArrowDepth) {
         Diag(OpLoc, diag::err_operator_arrow_depth_exceeded)
           << StartingType << getLangOpts().ArrowDepth << Base->getSourceRange();
@@ -8003,7 +7991,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
       }
 
       Result = BuildOverloadedArrowExpr(
-          S, Base, OpLoc,
+          Base, OpLoc,
           // When in a template specialization and on the first loop iteration,
           // potentially give the default diagnostic (with the fixit in a
           // separate note) instead of having the error reported back to here
@@ -8029,7 +8017,14 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
           }
         }
         return ExprError();
+      } else if (Result.isUnset()) {
+        // BuildOverloadedArrowExpr returns an empty expression to indicate
+        // that we need to build a dependent overloaded arrow expression.
+        assert(BaseType->isDependentType());
+        BaseType = Context.DependentTy;
+        break;
       }
+
       Base = Result.get();
       if (CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(Base))
         OperatorArrows.push_back(OpCall->getDirectCallee());
@@ -8067,7 +8062,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
   // it's legal for the type to be incomplete if this is a pseudo-destructor
   // call.  We'll do more incomplete-type checks later in the lookup process,
   // so just skip this check for ObjC types.
-  if (!BaseType->isRecordType()) {
+  if (!BaseType->getAsRecordDecl()) {
     ObjectType = ParsedType::make(BaseType);
     MayBePseudoDestructor = true;
     return Base;
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 861b0a91240b3b..c09059f4c5510b 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -15878,10 +15878,9 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
   return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), Method);
 }
 
-ExprResult
-Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
-                               bool *NoArrowOperatorFound) {
-  assert(Base->getType()->isRecordType() &&
+ExprResult Sema::BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc,
+                                          bool *NoArrowOperatorFound) {
+  assert(Base->getType()->getAsRecordDecl() &&
          "left-hand side must have class type");
 
   if (checkPlaceholderForOverload(*this, Base))
@@ -15904,7 +15903,12 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
     return ExprError();
 
   LookupResult R(*this, OpName, OpLoc, LookupOrdinaryName);
-  LookupQualifiedName(R, Base->getType()->castAs<RecordType>()->getDecl());
+  LookupParsedName(R, /*S=*/nullptr, /*SS=*/nullptr, Base->getType());
+
+  if (R.wasNotFoundInCurrentInstantiation() ||
+      (Base->isTypeDependent() && !R.empty()))
+    return ExprEmpty();
+
   R.suppressAccessDiagnostics();
 
   for (LookupResult::iterator Oper = R.begin(), OperEnd = R.end();
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 0daf620b4123e4..c42603a8925c47 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16583,10 +16583,10 @@ ExprResult TreeTransform<Derived>::RebuildCXXOperatorCallExpr(
   } else if (Op == OO_Arrow) {
     // It is possible that the type refers to a RecoveryExpr created earlier
     // in the tree transformation.
-    if (First->getType()->isDependentType())
+    if (First->containsErrors())
       return ExprError();
     // -> is never a builtin operation.
-    return SemaRef.BuildOverloadedArrowExpr(nullptr, First, OpLoc);
+    return SemaRef.BuildOverloadedArrowExpr(First, OpLoc);
   } else if (Second == nullptr || isPostIncDec) {
     if (!First->getType()->isOverloadableType() ||
         (Op == OO_Amp && getSema().isQualifiedMemberAccess(First))) {
diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp
index f32f49ef4539a5..89c22a0bc137d9 100644
--- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp
+++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp
@@ -484,16 +484,19 @@ namespace N4 {
   template<typename T>
   struct A {
     void not_instantiated(A a, A<T> b, T c) {
-      a->x;
-      b->x;
+      a->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}}
+      b->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}}
       c->x;
     }
 
     void instantiated(A a, A<T> b, T c) {
-      a->x; // expected-error {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}}
-            // expected-error at -1 {{no member named 'x' in 'N4::A<int>'}}
-      b->x; // expected-error {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}}
-            // expected-error at -1 {{no member named 'x' in 'N4::A<int>'}}
+      // FIXME: We should only emit a single diagnostic suggesting to use '.'!
+      a->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}}
+            // expected-error at -1 {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}}
+            // expected-error at -2 {{no member named 'x' in 'N4::A<int>'}}
+      b->x; // expected-error {{member reference type 'A<T>' is not a pointer; did you mean to use '.'?}}
+            // expected-error at -1 {{member reference type 'A<int>' is not a pointer; did you mean to use '.'?}}
+            // expected-error at -2 {{no member named 'x' in 'N4::A<int>'}}
       c->x; // expected-error {{member reference type 'int' is not a pointer}}
     }
   };
@@ -540,11 +543,10 @@ namespace N4 {
       a->T::f();
       a->T::g();
 
-      // FIXME: 'U' should be a dependent name, and its lookup context should be 'a.operator->()'!
-      a->U::x; // expected-error {{use of undeclared identifier 'U'}}
-      a->U::y; // expected-error {{use of undeclared identifier 'U'}}
-      a->U::f(); // expected-error {{use of undeclared identifier 'U'}}
-      a->U::g(); // expected-error {{use of undeclared identifier 'U'}}
+      a->U::x;
+      a->U::y;
+      a->U::f();
+      a->U::g();
     }
 
     void instantiated(D a) {

>From 0211427d45edff4d2991dd90e98b19b0fe794129 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Thu, 15 Aug 2024 13:24:27 -0400
Subject: [PATCH 2/4] [FOLD] fix destructor lookup when the object expression
 is dependent

---
 clang/lib/Sema/SemaExprCXX.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index f6e09f62cd3782..41f2cd1ed00b7e 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -8080,6 +8080,10 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
     return CreateRecoveryExpr(Base->getBeginLoc(), Base->getEndLoc(), {Base});
   }
 
+  // We can't implicitly declare the destructor for a templated class.
+  if (BaseType->isDependentType())
+    MayBePseudoDestructor = true;
+
   // C++ [basic.lookup.classref]p2:
   //   If the id-expression in a class member access (5.2.5) is an
   //   unqualified-id, and the type of the object expression is of a class

>From 29282d20ec95922322c41992cffb00e6b4fc8a69 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Tue, 3 Sep 2024 12:17:39 -0400
Subject: [PATCH 3/4] [FOLD] use IsDependent output parameter

---
 clang/include/clang/Sema/Sema.h |  3 ++-
 clang/lib/Sema/SemaExprCXX.cpp  | 19 ++++++++++++-------
 clang/lib/Sema/SemaOverload.cpp | 16 +++++++++++++---
 clang/lib/Sema/TreeTransform.h  |  6 +++++-
 4 files changed, 32 insertions(+), 12 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 0666640584f53a..c1afefd36ce0ce 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10640,7 +10640,8 @@ class Sema final : public SemaBase {
   ///  (if one exists), where @c Base is an expression of class type and
   /// @c Member is the name of the member we're trying to find.
   ExprResult BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc,
-                                      bool *NoArrowOperatorFound = nullptr);
+                                      bool *NoArrowOperatorFound,
+                                      bool &IsDependent);
 
   ExprResult BuildCXXMemberCallExpr(Expr *Exp, NamedDecl *FoundDecl,
                                     CXXConversionDecl *Method,
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 41f2cd1ed00b7e..56b822b8fef223 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -7990,6 +7990,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
         return ExprError();
       }
 
+      bool IsDependent;
       Result = BuildOverloadedArrowExpr(
           Base, OpLoc,
           // When in a template specialization and on the first loop iteration,
@@ -7998,7 +7999,17 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
           // and giving a diagnostic with a fixit attached to the error itself.
           (FirstIteration && CurFD && CurFD->isFunctionTemplateSpecialization())
               ? nullptr
-              : &NoArrowOperatorFound);
+              : &NoArrowOperatorFound,
+          IsDependent);
+
+      if (IsDependent) {
+        // BuildOverloadedArrowExpr sets IsDependent to indicate that we need
+        // to build a dependent overloaded arrow expression.
+        assert(BaseType->isDependentType());
+        BaseType = Context.DependentTy;
+        break;
+      }
+
       if (Result.isInvalid()) {
         if (NoArrowOperatorFound) {
           if (FirstIteration) {
@@ -8017,12 +8028,6 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
           }
         }
         return ExprError();
-      } else if (Result.isUnset()) {
-        // BuildOverloadedArrowExpr returns an empty expression to indicate
-        // that we need to build a dependent overloaded arrow expression.
-        assert(BaseType->isDependentType());
-        BaseType = Context.DependentTy;
-        break;
       }
 
       Base = Result.get();
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index c09059f4c5510b..87202bf118bee2 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -15879,10 +15879,13 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
 }
 
 ExprResult Sema::BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc,
-                                          bool *NoArrowOperatorFound) {
+                                          bool *NoArrowOperatorFound,
+                                          bool &IsDependent) {
   assert(Base->getType()->getAsRecordDecl() &&
          "left-hand side must have class type");
 
+  IsDependent = false;
+
   if (checkPlaceholderForOverload(*this, Base))
     return ExprError();
 
@@ -15905,9 +15908,16 @@ ExprResult Sema::BuildOverloadedArrowExpr(Expr *Base, SourceLocation OpLoc,
   LookupResult R(*this, OpName, OpLoc, LookupOrdinaryName);
   LookupParsedName(R, /*S=*/nullptr, /*SS=*/nullptr, Base->getType());
 
+  // If the expression is dependent and we either:
+  // - Found a member of the current instantiation named 'operator->', or
+  // - Found nothing, and the lookup context has no dependent base classes
+  //
+  // Then we should build a dependent class member access expression.
   if (R.wasNotFoundInCurrentInstantiation() ||
-      (Base->isTypeDependent() && !R.empty()))
-    return ExprEmpty();
+      (Base->isTypeDependent() && !R.empty())) {
+    IsDependent = true;
+    return Base;
+  }
 
   R.suppressAccessDiagnostics();
 
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index c42603a8925c47..235b51a33babd5 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16585,8 +16585,12 @@ ExprResult TreeTransform<Derived>::RebuildCXXOperatorCallExpr(
     // in the tree transformation.
     if (First->containsErrors())
       return ExprError();
+    bool IsDependent;
     // -> is never a builtin operation.
-    return SemaRef.BuildOverloadedArrowExpr(First, OpLoc);
+    ExprResult Result = SemaRef.BuildOverloadedArrowExpr(
+        First, OpLoc, /*NoArrowOperatorFound=*/nullptr, IsDependent);
+    assert(!IsDependent);
+    return Result;
   } else if (Second == nullptr || isPostIncDec) {
     if (!First->getType()->isOverloadableType() ||
         (Op == OO_Amp && getSema().isQualifiedMemberAccess(First))) {

>From 0b4a403f0296ed882a4da755019445a1f33f1f07 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Wed, 4 Sep 2024 09:43:22 -0400
Subject: [PATCH 4/4] [FOLD] don't use getAsRecordDecl

---
 clang/lib/Sema/SemaExprCXX.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 56b822b8fef223..3c7eac116e5dc9 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -7980,7 +7980,8 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
     SmallVector<FunctionDecl*, 8> OperatorArrows;
     CTypes.insert(Context.getCanonicalType(BaseType));
 
-    while (BaseType->getAsRecordDecl()) {
+    while (
+        isa<InjectedClassNameType, RecordType>(BaseType.getCanonicalType())) {
       if (OperatorArrows.size() >= getLangOpts().ArrowDepth) {
         Diag(OpLoc, diag::err_operator_arrow_depth_exceeded)
           << StartingType << getLangOpts().ArrowDepth << Base->getSourceRange();
@@ -8067,7 +8068,7 @@ ExprResult Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base,
   // it's legal for the type to be incomplete if this is a pseudo-destructor
   // call.  We'll do more incomplete-type checks later in the lookup process,
   // so just skip this check for ObjC types.
-  if (!BaseType->getAsRecordDecl()) {
+  if (!isa<InjectedClassNameType, RecordType>(BaseType.getCanonicalType())) {
     ObjectType = ParsedType::make(BaseType);
     MayBePseudoDestructor = true;
     return Base;



More information about the cfe-commits mailing list