[clang] 4a9523c - PR52537: When performing a no-op TreeTransform of a rewritten binary

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Wed Nov 17 18:19:54 PST 2021


Author: Richard Smith
Date: 2021-11-17T18:19:46-08:00
New Revision: 4a9523c55fa11d85a4edbf24abc2c17bb3849fbc

URL: https://github.com/llvm/llvm-project/commit/4a9523c55fa11d85a4edbf24abc2c17bb3849fbc
DIFF: https://github.com/llvm/llvm-project/commit/4a9523c55fa11d85a4edbf24abc2c17bb3849fbc.diff

LOG: PR52537: When performing a no-op TreeTransform of a rewritten binary
operator, mark any functions it calls as referenced.

Added: 
    

Modified: 
    clang/include/clang/Sema/Sema.h
    clang/lib/Sema/SemaExpr.cpp
    clang/lib/Sema/TreeTransform.h
    clang/test/SemaCXX/compare-cxx2a.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index a159be2b5fb17..91c9feb40cbae 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5114,7 +5114,8 @@ class Sema final {
   /// type -- entities referenced by the type are now referenced.
   void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T);
   void MarkDeclarationsReferencedInExpr(Expr *E,
-                                        bool SkipLocalVariables = false);
+                                        bool SkipLocalVariables = false,
+                                        ArrayRef<const Expr *> StopAt = None);
 
   /// Try to recover by turning the given expression into a
   /// call.  Returns true if recovery was attempted or an error was

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 3297c01047ad4..97f2062d44851 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18867,14 +18867,22 @@ class EvaluatedExprMarker : public UsedDeclVisitor<EvaluatedExprMarker> {
 public:
   typedef UsedDeclVisitor<EvaluatedExprMarker> Inherited;
   bool SkipLocalVariables;
+  ArrayRef<const Expr *> StopAt;
 
-  EvaluatedExprMarker(Sema &S, bool SkipLocalVariables)
-      : Inherited(S), SkipLocalVariables(SkipLocalVariables) {}
+  EvaluatedExprMarker(Sema &S, bool SkipLocalVariables,
+                      ArrayRef<const Expr *> StopAt)
+      : Inherited(S), SkipLocalVariables(SkipLocalVariables), StopAt(StopAt) {}
 
   void visitUsedDecl(SourceLocation Loc, Decl *D) {
     S.MarkFunctionReferenced(Loc, cast<FunctionDecl>(D));
   }
 
+  void Visit(Expr *E) {
+    if (std::find(StopAt.begin(), StopAt.end(), E) != StopAt.end())
+      return;
+    Inherited::Visit(E);
+  }
+
   void VisitDeclRefExpr(DeclRefExpr *E) {
     // If we were asked not to visit local variables, don't.
     if (SkipLocalVariables) {
@@ -18901,9 +18909,11 @@ class EvaluatedExprMarker : public UsedDeclVisitor<EvaluatedExprMarker> {
 ///
 /// \param SkipLocalVariables If true, don't mark local variables as
 /// 'referenced'.
+/// \param StopAt Subexpressions that we shouldn't recurse into.
 void Sema::MarkDeclarationsReferencedInExpr(Expr *E,
-                                            bool SkipLocalVariables) {
-  EvaluatedExprMarker(*this, SkipLocalVariables).Visit(E);
+                                            bool SkipLocalVariables,
+                                            ArrayRef<const Expr*> StopAt) {
+  EvaluatedExprMarker(*this, SkipLocalVariables, StopAt).Visit(E);
 }
 
 /// Emit a diagnostic when statements are reachable.

diff  --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 442b4819b808c..7f3326c13263f 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -11008,14 +11008,10 @@ ExprResult TreeTransform<Derived>::TransformCXXRewrittenBinaryOperator(
   if (RHS.isInvalid())
     return ExprError();
 
-  if (!getDerived().AlwaysRebuild() &&
-      LHS.get() == Decomp.LHS &&
-      RHS.get() == Decomp.RHS)
-    return E;
-
   // Extract the already-resolved callee declarations so that we can restrict
   // ourselves to using them as the unqualified lookup results when rebuilding.
   UnresolvedSet<2> UnqualLookups;
+  bool ChangedAnyLookups = false;
   Expr *PossibleBinOps[] = {E->getSemanticForm(),
                             const_cast<Expr *>(Decomp.InnerBinOp)};
   for (Expr *PossibleBinOp : PossibleBinOps) {
@@ -11032,9 +11028,23 @@ ExprResult TreeTransform<Derived>::TransformCXXRewrittenBinaryOperator(
         E->getOperatorLoc(), Callee->getFoundDecl()));
     if (!Found)
       return ExprError();
+    if (Found != Callee->getFoundDecl())
+      ChangedAnyLookups = true;
     UnqualLookups.addDecl(Found);
   }
 
+  if (!getDerived().AlwaysRebuild() && !ChangedAnyLookups &&
+      LHS.get() == Decomp.LHS && RHS.get() == Decomp.RHS) {
+    // Mark all functions used in the rewrite as referenced. Note that when
+    // a < b is rewritten to (a <=> b) < 0, both the <=> and the < might be
+    // function calls, and/or there might be a user-defined conversion sequence
+    // applied to the operands of the <.
+    // FIXME: this is a bit instantiation-specific.
+    const Expr *StopAt[] = {Decomp.LHS, Decomp.RHS};
+    SemaRef.MarkDeclarationsReferencedInExpr(E, false, StopAt);
+    return E;
+  }
+
   return getDerived().RebuildCXXRewrittenBinaryOperator(
       E->getOperatorLoc(), Decomp.Opcode, UnqualLookups, LHS.get(), RHS.get());
 }

diff  --git a/clang/test/SemaCXX/compare-cxx2a.cpp b/clang/test/SemaCXX/compare-cxx2a.cpp
index 61b8558ccc02c..0cb48bcfcec27 100644
--- a/clang/test/SemaCXX/compare-cxx2a.cpp
+++ b/clang/test/SemaCXX/compare-cxx2a.cpp
@@ -425,3 +425,39 @@ namespace PR44992 {
     friend auto operator<=>(s const &, s const &) = default;
   };
 }
+
+namespace PR52537 {
+  template<typename T> struct X {};
+  template<typename T> bool operator==(const X<T> &, int) { return T::error; } // expected-error 2{{no members}}
+  template<typename T> int operator<=>(const X<T> &, int) { return T::error; } // expected-error 2{{no members}}
+
+  const X<int[1]> x1;
+  template<typename T> bool f1() { return x1 != 0; } // expected-note {{instantiation of}}
+  void g1() { f1<int>(); } // expected-note {{instantiation of}}
+
+  const X<int[2]> x2;
+  template<typename T> bool f2() { return 0 == x2; } // expected-note {{instantiation of}}
+  void g2() { f2<int>(); } // expected-note {{instantiation of}}
+
+  const X<int[3]> x3;
+  template<typename T> bool f3() { return x3 < 0; } // expected-note {{instantiation of}}
+  void g3() { f3<int>(); } // expected-note {{instantiation of}}
+
+  const X<int[4]> x4;
+  template<typename T> bool f4() { return 0 >= x4; } // expected-note {{instantiation of}}
+  void g4() { f4<int>(); } // expected-note {{instantiation of}}
+
+  template<typename T> struct Y {};
+  template<typename T> struct Z { Z(int) { T::error; } using nondeduced = Z; }; // expected-error 2{{no members}}
+  template<typename T> Z<T> operator<=>(const Y<T>&, int);
+  template<typename T> bool operator<(const Z<T>&, const typename Z<T>::nondeduced&);
+  template<typename T> bool operator<(const typename Z<T>::nondeduced&, const Z<T>&);
+
+  const Y<int[5]> y5;
+  template<typename T> bool f5() { return y5 < 0; } // expected-note {{instantiation of}}
+  void g5() { f5<int>(); } // expected-note {{instantiation of}}
+
+  const Y<int[6]> y6;
+  template<typename T> bool f6() { return 0 < y6; } // expected-note {{instantiation of}}
+  void g6() { f6<int>(); } // expected-note {{instantiation of}}
+}


        


More information about the cfe-commits mailing list