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}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits