Quuxplusone created this revision. Quuxplusone added reviewers: ldionne, rsmith, leonardchan, mizvekov. Quuxplusone added a project: clang. Quuxplusone requested review of this revision. Herald added a subscriber: cfe-commits.
If this is a SFINAE context, then continuing to look up names (in particular, to treat a non-function as a function, and then do ADL) might too-eagerly complete a type that it's not safe to complete right now. We should just say "okay, that's a substitution failure" and not do any more work than absolutely required. Fixes https://github.com/llvm/llvm-project/issues/52970 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D117603 Files: clang/lib/Sema/Sema.cpp clang/lib/Sema/SemaExprMember.cpp clang/test/SemaCXX/PR52970.cpp Index: clang/test/SemaCXX/PR52970.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/PR52970.cpp @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++14 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s +// expected-no-diagnostics + +struct Incomplete; +template<class T> struct Holder { T t; }; + +namespace DotFollowingFunctionName { + struct Good { + struct Nested { + int b; + } a; + }; + + struct Bad { + Holder<Incomplete> a(); + }; + + template<class T> + constexpr auto f(T t) -> decltype((t.a.b, true)) + { return true; } + constexpr bool f(...) { return false; } + + static_assert(DotFollowingFunctionName::f(Good{}), ""); + static_assert(!DotFollowingFunctionName::f(Bad{}), ""); + +#if __cplusplus >= 202002L + template<class T> + concept C = requires (T t) { t.a.b; }; + + static_assert(C<Good>); + static_assert(!C<Bad>); +#endif +} + +namespace DotFollowingPointer { + struct Good { + int begin(); + }; + using Bad = Holder<Incomplete>*; + + template<class T> + constexpr auto f(T t) -> decltype((t.begin(), true)) + { return true; } + constexpr bool f(...) { return false; } + + static_assert(DotFollowingPointer::f(Good{}), ""); + static_assert(!DotFollowingPointer::f(Bad{}), ""); + +#if __cplusplus >= 202002L + template<class T> + concept C = requires (T t) { t.begin(); }; + + static_assert(C<Good>); + static_assert(!C<Bad>); +#endif +} Index: clang/lib/Sema/SemaExprMember.cpp =================================================================== --- clang/lib/Sema/SemaExprMember.cpp +++ clang/lib/Sema/SemaExprMember.cpp @@ -1645,6 +1645,9 @@ << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange() << FixItHint::CreateReplacement(OpLoc, "->"); + if (S.isSFINAEContext()) + return ExprError(); + // Recurse as an -> access. IsArrow = true; return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS, Index: clang/lib/Sema/Sema.cpp =================================================================== --- clang/lib/Sema/Sema.cpp +++ clang/lib/Sema/Sema.cpp @@ -2552,6 +2552,10 @@ bool Sema::tryToRecoverWithCall(ExprResult &E, const PartialDiagnostic &PD, bool ForceComplain, bool (*IsPlausibleResult)(QualType)) { + if (isSFINAEContext()) { + // If this is a SFINAE context, don't try anything that might trigger ADL prematurely. + return false; + } SourceLocation Loc = E.get()->getExprLoc(); SourceRange Range = E.get()->getSourceRange();
Index: clang/test/SemaCXX/PR52970.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/PR52970.cpp @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++14 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s +// expected-no-diagnostics + +struct Incomplete; +template<class T> struct Holder { T t; }; + +namespace DotFollowingFunctionName { + struct Good { + struct Nested { + int b; + } a; + }; + + struct Bad { + Holder<Incomplete> a(); + }; + + template<class T> + constexpr auto f(T t) -> decltype((t.a.b, true)) + { return true; } + constexpr bool f(...) { return false; } + + static_assert(DotFollowingFunctionName::f(Good{}), ""); + static_assert(!DotFollowingFunctionName::f(Bad{}), ""); + +#if __cplusplus >= 202002L + template<class T> + concept C = requires (T t) { t.a.b; }; + + static_assert(C<Good>); + static_assert(!C<Bad>); +#endif +} + +namespace DotFollowingPointer { + struct Good { + int begin(); + }; + using Bad = Holder<Incomplete>*; + + template<class T> + constexpr auto f(T t) -> decltype((t.begin(), true)) + { return true; } + constexpr bool f(...) { return false; } + + static_assert(DotFollowingPointer::f(Good{}), ""); + static_assert(!DotFollowingPointer::f(Bad{}), ""); + +#if __cplusplus >= 202002L + template<class T> + concept C = requires (T t) { t.begin(); }; + + static_assert(C<Good>); + static_assert(!C<Bad>); +#endif +} Index: clang/lib/Sema/SemaExprMember.cpp =================================================================== --- clang/lib/Sema/SemaExprMember.cpp +++ clang/lib/Sema/SemaExprMember.cpp @@ -1645,6 +1645,9 @@ << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange() << FixItHint::CreateReplacement(OpLoc, "->"); + if (S.isSFINAEContext()) + return ExprError(); + // Recurse as an -> access. IsArrow = true; return LookupMemberExpr(S, R, BaseExpr, IsArrow, OpLoc, SS, Index: clang/lib/Sema/Sema.cpp =================================================================== --- clang/lib/Sema/Sema.cpp +++ clang/lib/Sema/Sema.cpp @@ -2552,6 +2552,10 @@ bool Sema::tryToRecoverWithCall(ExprResult &E, const PartialDiagnostic &PD, bool ForceComplain, bool (*IsPlausibleResult)(QualType)) { + if (isSFINAEContext()) { + // If this is a SFINAE context, don't try anything that might trigger ADL prematurely. + return false; + } SourceLocation Loc = E.get()->getExprLoc(); SourceRange Range = E.get()->getSourceRange();
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits