[PATCH] D80109: [AST][RecoveryExpr] Preserve type for broken member call expr.
This revision was landed with ongoing or failed builds. This revision was automatically updated to reflect the committed changes. Closed by commit rG6326b098852b: [AST][RecoveryExpr] Preserve type for broken overrload member call expr. (authored by hokein). Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D80109/new/ https://reviews.llvm.org/D80109 Files: clang/lib/Sema/SemaOverload.cpp clang/test/AST/ast-dump-recovery.cpp clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp Index: clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp === --- clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp +++ clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp @@ -9,7 +9,7 @@ // parameter types in a base class (rather than conflicting). template struct Opaque {}; -template void expect(Opaque _) {} +template void expect(Opaque _) {} // expected-note 4 {{candidate function template not viable}} // PR5727 // This just shouldn't crash. @@ -134,14 +134,14 @@ void test() { expect<0>(Base().foo()); expect<1>(Base().foo<0>()); -expect<0>(Derived1().foo()); // expected-error {{no matching member function for call to 'foo'}} +expect<0>(Derived1().foo()); // expected-error {{no matching member function for call to 'foo'}} expected-error {{no matching function for call to 'expect'}} expect<2>(Derived1().foo<0>()); -expect<0>(Derived2().foo()); // expected-error {{no matching member function for call to 'foo'}} +expect<0>(Derived2().foo()); // expected-error {{no matching member function for call to 'foo'}} expected-error {{no matching function for call to 'expect'}} expect<2>(Derived2().foo<0>()); expect<3>(Derived3().foo()); -expect<1>(Derived3().foo<0>()); // expected-error {{no matching member function for call to 'foo'}} +expect<1>(Derived3().foo<0>()); // expected-error {{no matching member function for call to 'foo'}} expected-error {{no matching function for call to 'expect'}} expect<3>(Derived4().foo()); -expect<1>(Derived4().foo<0>()); // expected-error {{no matching member function for call to 'foo'}} +expect<1>(Derived4().foo<0>()); // expected-error {{no matching member function for call to 'foo'}} expected-error {{no matching function for call to 'expect'}} } } Index: clang/test/AST/ast-dump-recovery.cpp === --- clang/test/AST/ast-dump-recovery.cpp +++ clang/test/AST/ast-dump-recovery.cpp @@ -125,6 +125,9 @@ double func(); class ForwardClass; ForwardClass createFwd(); + + int overload(); + int overload(int, int); }; void test2(Foo2 f) { // CHECK: RecoveryExpr {{.*}} 'double' @@ -136,6 +139,11 @@ // CHECK-NEXT: `-MemberExpr {{.*}} '' .createFwd // CHECK-NEXT: `-DeclRefExpr {{.*}} 'f' f.createFwd(); + // CHECK: RecoveryExpr {{.*}} 'int' contains-errors + // CHECK-NEXT: |-UnresolvedMemberExpr + // CHECK-NEXT:`-DeclRefExpr {{.*}} 'Foo2' + // CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1 + f.overload(1); } // CHECK: |-AlignedAttr {{.*}} alignas Index: clang/lib/Sema/SemaOverload.cpp === --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -14300,6 +14300,7 @@ UnbridgedCasts.restore(); OverloadCandidateSet::iterator Best; +bool Succeeded = false; switch (CandidateSet.BestViableFunction(*this, UnresExpr->getBeginLoc(), Best)) { case OR_Success: @@ -14307,7 +14308,7 @@ FoundDecl = Best->FoundDecl; CheckUnresolvedMemberAccess(UnresExpr, Best->FoundDecl); if (DiagnoseUseOfDecl(Best->FoundDecl, UnresExpr->getNameLoc())) -return ExprError(); +break; // If FoundDecl is different from Method (such as if one is a template // and the other a specialization), make sure DiagnoseUseOfDecl is // called on both. @@ -14316,7 +14317,8 @@ // being used. if (Method != FoundDecl.getDecl() && DiagnoseUseOfDecl(Method, UnresExpr->getNameLoc())) -return ExprError(); +break; + Succeeded = true; break; case OR_No_Viable_Function: @@ -14326,27 +14328,25 @@ PDiag(diag::err_ovl_no_viable_member_function_in_call) << DeclName << MemExprE->getSourceRange()), *this, OCD_AllCandidates, Args); - // FIXME: Leaking incoming expressions! - return ExprError(); - + break; case OR_Ambiguous: CandidateSet.NoteCandidates( PartialDiagnosticAt(UnresExpr->getMemberLoc(), PDiag(diag::err_ovl_ambiguous_member_call) << DeclName << MemExprE->getSourceRange()), *this,
[PATCH] D80109: [AST][RecoveryExpr] Preserve type for broken member call expr.
hokein updated this revision to Diff 311148. hokein added a comment. rebase Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D80109/new/ https://reviews.llvm.org/D80109 Files: clang/lib/Sema/SemaOverload.cpp clang/test/AST/ast-dump-recovery.cpp clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp Index: clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp === --- clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp +++ clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp @@ -9,7 +9,7 @@ // parameter types in a base class (rather than conflicting). template struct Opaque {}; -template void expect(Opaque _) {} +template void expect(Opaque _) {} // expected-note 4 {{candidate function template not viable}} // PR5727 // This just shouldn't crash. @@ -134,14 +134,14 @@ void test() { expect<0>(Base().foo()); expect<1>(Base().foo<0>()); -expect<0>(Derived1().foo()); // expected-error {{no matching member function for call to 'foo'}} +expect<0>(Derived1().foo()); // expected-error {{no matching member function for call to 'foo'}} expected-error {{no matching function for call to 'expect'}} expect<2>(Derived1().foo<0>()); -expect<0>(Derived2().foo()); // expected-error {{no matching member function for call to 'foo'}} +expect<0>(Derived2().foo()); // expected-error {{no matching member function for call to 'foo'}} expected-error {{no matching function for call to 'expect'}} expect<2>(Derived2().foo<0>()); expect<3>(Derived3().foo()); -expect<1>(Derived3().foo<0>()); // expected-error {{no matching member function for call to 'foo'}} +expect<1>(Derived3().foo<0>()); // expected-error {{no matching member function for call to 'foo'}} expected-error {{no matching function for call to 'expect'}} expect<3>(Derived4().foo()); -expect<1>(Derived4().foo<0>()); // expected-error {{no matching member function for call to 'foo'}} +expect<1>(Derived4().foo<0>()); // expected-error {{no matching member function for call to 'foo'}} expected-error {{no matching function for call to 'expect'}} } } Index: clang/test/AST/ast-dump-recovery.cpp === --- clang/test/AST/ast-dump-recovery.cpp +++ clang/test/AST/ast-dump-recovery.cpp @@ -125,6 +125,9 @@ double func(); class ForwardClass; ForwardClass createFwd(); + + int overload(); + int overload(int, int); }; void test2(Foo2 f) { // CHECK: RecoveryExpr {{.*}} 'double' @@ -136,6 +139,11 @@ // CHECK-NEXT: `-MemberExpr {{.*}} '' .createFwd // CHECK-NEXT: `-DeclRefExpr {{.*}} 'f' f.createFwd(); + // CHECK: RecoveryExpr {{.*}} 'int' contains-errors + // CHECK-NEXT: |-UnresolvedMemberExpr + // CHECK-NEXT:`-DeclRefExpr {{.*}} 'Foo2' + // CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1 + f.overload(1); } // CHECK: |-AlignedAttr {{.*}} alignas Index: clang/lib/Sema/SemaOverload.cpp === --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -14300,6 +14300,7 @@ UnbridgedCasts.restore(); OverloadCandidateSet::iterator Best; +bool Succeeded = false; switch (CandidateSet.BestViableFunction(*this, UnresExpr->getBeginLoc(), Best)) { case OR_Success: @@ -14307,7 +14308,7 @@ FoundDecl = Best->FoundDecl; CheckUnresolvedMemberAccess(UnresExpr, Best->FoundDecl); if (DiagnoseUseOfDecl(Best->FoundDecl, UnresExpr->getNameLoc())) -return ExprError(); +break; // If FoundDecl is different from Method (such as if one is a template // and the other a specialization), make sure DiagnoseUseOfDecl is // called on both. @@ -14316,7 +14317,8 @@ // being used. if (Method != FoundDecl.getDecl() && DiagnoseUseOfDecl(Method, UnresExpr->getNameLoc())) -return ExprError(); +break; + Succeeded = true; break; case OR_No_Viable_Function: @@ -14326,27 +14328,25 @@ PDiag(diag::err_ovl_no_viable_member_function_in_call) << DeclName << MemExprE->getSourceRange()), *this, OCD_AllCandidates, Args); - // FIXME: Leaking incoming expressions! - return ExprError(); - + break; case OR_Ambiguous: CandidateSet.NoteCandidates( PartialDiagnosticAt(UnresExpr->getMemberLoc(), PDiag(diag::err_ovl_ambiguous_member_call) << DeclName << MemExprE->getSourceRange()), *this, OCD_AmbiguousCandidates, Args); - // FIXME: Leaking incoming expressions! - return ExprError(); - + break; case OR_Deleted: CandidateSet.NoteCandidates(
[PATCH] D80109: [AST][RecoveryExpr] Preserve type for broken member call expr.
hokein marked 2 inline comments as done. hokein added a comment. sorry for the delay, picking it up now. Comment at: clang/lib/Sema/SemaCoroutine.cpp:864 ExprResult R = buildPromiseCall(*this, Promise, Loc, "await_transform", E); -if (R.isInvalid()) { +if (R.isInvalid() || R.get()->containsErrors()) { Diag(Loc, we need this change to prevent a regression on co_await-range-for. without this change, we will emit 2 `call to deleted member function 'await_transform'` diagnostics on https://github.com/llvm/llvm-project/blob/master/clang/test/SemaCXX/co_await-range-for.cpp#L52-L54. Comment at: clang/test/AST/ast-dump-recovery.cpp:123 + + // FIXME: capture the type! + f.func(1); sammccall wrote: > why does this not work? > isn't there only one candidate, so chooseRecoveryType should pick it? this is on a different codepath, will fix in https://reviews.llvm.org/D92298 Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D80109/new/ https://reviews.llvm.org/D80109 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D80109: [AST][RecoveryExpr] Preserve type for broken member call expr.
hokein updated this revision to Diff 308267. hokein added a comment. Herald added a subscriber: lxfind. rebase and address comments. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D80109/new/ https://reviews.llvm.org/D80109 Files: clang/lib/Sema/SemaCoroutine.cpp clang/lib/Sema/SemaOverload.cpp clang/test/AST/ast-dump-recovery.cpp clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp Index: clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp === --- clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp +++ clang/test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p12.cpp @@ -9,7 +9,7 @@ // parameter types in a base class (rather than conflicting). template struct Opaque {}; -template void expect(Opaque _) {} +template void expect(Opaque _) {} // expected-note 4 {{candidate function template not viable}} // PR5727 // This just shouldn't crash. @@ -134,14 +134,14 @@ void test() { expect<0>(Base().foo()); expect<1>(Base().foo<0>()); -expect<0>(Derived1().foo()); // expected-error {{no matching member function for call to 'foo'}} +expect<0>(Derived1().foo()); // expected-error {{no matching member function for call to 'foo'}} expected-error {{no matching function for call to 'expect'}} expect<2>(Derived1().foo<0>()); -expect<0>(Derived2().foo()); // expected-error {{no matching member function for call to 'foo'}} +expect<0>(Derived2().foo()); // expected-error {{no matching member function for call to 'foo'}} expected-error {{no matching function for call to 'expect'}} expect<2>(Derived2().foo<0>()); expect<3>(Derived3().foo()); -expect<1>(Derived3().foo<0>()); // expected-error {{no matching member function for call to 'foo'}} +expect<1>(Derived3().foo<0>()); // expected-error {{no matching member function for call to 'foo'}} expected-error {{no matching function for call to 'expect'}} expect<3>(Derived4().foo()); -expect<1>(Derived4().foo<0>()); // expected-error {{no matching member function for call to 'foo'}} +expect<1>(Derived4().foo<0>()); // expected-error {{no matching member function for call to 'foo'}} expected-error {{no matching function for call to 'expect'}} } } Index: clang/test/AST/ast-dump-recovery.cpp === --- clang/test/AST/ast-dump-recovery.cpp +++ clang/test/AST/ast-dump-recovery.cpp @@ -121,6 +121,21 @@ foo->func(x); } +// CHECK: FunctionDecl {{.*}} test2 +// CHECK-NEXT: |-ParmVarDecl {{.*}} f +// CHECK-NEXT: `-CompoundStmt +// CHECK-NEXT:-RecoveryExpr {{.*}} 'int' contains-errors +// CHECK-NEXT:|-UnresolvedMemberExpr +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'Foo2' +// CHECK-NEXT:`-IntegerLiteral {{.*}} 'int' 1 +struct Foo2 { + int overload(); + int overload(int, int); +}; +void test2(Foo2 f) { + f.overload(1); // verify "int" is preserved +} + // CHECK: |-AlignedAttr {{.*}} alignas // CHECK-NEXT:| `-RecoveryExpr {{.*}} contains-errors // CHECK-NEXT:| `-UnresolvedLookupExpr {{.*}} 'invalid' Index: clang/lib/Sema/SemaOverload.cpp === --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -14239,6 +14239,7 @@ UnbridgedCasts.restore(); OverloadCandidateSet::iterator Best; +bool Succeeded = false; switch (CandidateSet.BestViableFunction(*this, UnresExpr->getBeginLoc(), Best)) { case OR_Success: @@ -14246,7 +14247,7 @@ FoundDecl = Best->FoundDecl; CheckUnresolvedMemberAccess(UnresExpr, Best->FoundDecl); if (DiagnoseUseOfDecl(Best->FoundDecl, UnresExpr->getNameLoc())) -return ExprError(); +break; // If FoundDecl is different from Method (such as if one is a template // and the other a specialization), make sure DiagnoseUseOfDecl is // called on both. @@ -14255,7 +14256,8 @@ // being used. if (Method != FoundDecl.getDecl() && DiagnoseUseOfDecl(Method, UnresExpr->getNameLoc())) -return ExprError(); +break; + Succeeded = true; break; case OR_No_Viable_Function: @@ -14265,8 +14267,7 @@ PDiag(diag::err_ovl_no_viable_member_function_in_call) << DeclName << MemExprE->getSourceRange()), *this, OCD_AllCandidates, Args); - // FIXME: Leaking incoming expressions! - return ExprError(); + break; case OR_Ambiguous: CandidateSet.NoteCandidates( @@ -14274,8 +14275,7 @@ PDiag(diag::err_ovl_ambiguous_member_call) << DeclName << MemExprE->getSourceRange()), *this, OCD_AmbiguousCandidates, Args); - // FIXME: Leaking incoming expressions! - return
[PATCH] D80109: [AST][RecoveryExpr] Preserve type for broken member call expr.
sammccall added a comment. @hokein Did this get landed at some point? Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D80109/new/ https://reviews.llvm.org/D80109 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D80109: [AST][RecoveryExpr] Preserve type for broken member call expr.
sammccall accepted this revision. sammccall added inline comments. This revision is now accepted and ready to land. Comment at: clang/lib/Sema/SemaOverload.cpp:14083 OverloadCandidateSet::iterator Best; +bool Succeeded = true; switch (CandidateSet.BestViableFunction(*this, UnresExpr->getBeginLoc(), seems like `bool Success = false` would be a "safer" default and have to be set in fewer places Comment at: clang/lib/Sema/SemaOverload.cpp:14105 case OR_No_Viable_Function: CandidateSet.NoteCandidates( hokein wrote: > It is a bit sad that the broken function call cases (too many/few agguments) > are failing into this group, all candidates are not viable -- this means we > don't get any benefits (no `Best`), for some cases, it is suboptimal even > though the best candidate looks like obvious. > > ``` > class Collection { > const char *find(int) const; > char* find(int); > }; > void test1(const Collection ) { > C.find(); // we want const char*, but all overloads are not viable and > return types are not the same, so no type captured here. > } > ``` Yeah, at some point we can add more heuristics (discard non-viable or hidden functions using heuristics) to handle this case, like I did in SemaCodeComplete... Comment at: clang/lib/Sema/SemaOverload.cpp:14133 } +// Overload resolvation fails, try to recover. +if (!Succeeded) resolvation -> resolution Comment at: clang/test/AST/ast-dump-recovery.cpp:123 + + // FIXME: capture the type! + f.func(1); why does this not work? isn't there only one candidate, so chooseRecoveryType should pick it? Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D80109/new/ https://reviews.llvm.org/D80109 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D80109: [AST][RecoveryExpr] Preserve type for broken member call expr.
hokein marked an inline comment as done. hokein added inline comments. Comment at: clang/lib/Sema/SemaOverload.cpp:14105 case OR_No_Viable_Function: CandidateSet.NoteCandidates( It is a bit sad that the broken function call cases (too many/few agguments) are failing into this group, all candidates are not viable -- this means we don't get any benefits (no `Best`), for some cases, it is suboptimal even though the best candidate looks like obvious. ``` class Collection { const char *find(int) const; char* find(int); }; void test1(const Collection ) { C.find(); // we want const char*, but all overloads are not viable and return types are not the same, so no type captured here. } ``` Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D80109/new/ https://reviews.llvm.org/D80109 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D80109: [AST][RecoveryExpr] Preserve type for broken member call expr.
hokein created this revision. hokein added a reviewer: sammccall. Herald added a project: clang. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D80109 Files: clang/lib/Sema/SemaOverload.cpp clang/test/AST/ast-dump-recovery.cpp Index: clang/test/AST/ast-dump-recovery.cpp === --- clang/test/AST/ast-dump-recovery.cpp +++ clang/test/AST/ast-dump-recovery.cpp @@ -101,6 +101,29 @@ foo->func(x); } +// CHECK: FunctionDecl {{.*}} test2 +// CHECK-NEXT: |-ParmVarDecl {{.*}} f +// CHECK-NEXT: `-CompoundStmt +// CHECK-NEXT: |-RecoveryExpr {{.*}} 'int' contains-errors +// CHECK-NEXT: `-UnresolvedMemberExpr +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'Foo2' +// CHECK-NEXT: `-RecoveryExpr {{.*}} '' +// CHECK-NEXT: |-MemberExpr {{.*}} '' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} +// CHECK-NEXT: `-IntegerLiteral {{.*}} 'int' 1 +struct Foo2 { + int overload(int); + int overload(int, int); + + double func(); +}; +void test2(Foo2 f) { + f.overload(); // verify "int" is preserved + + // FIXME: capture the type! + f.func(1); +} + // CHECK: |-AlignedAttr {{.*}} alignas // CHECK-NEXT:| `-RecoveryExpr {{.*}} contains-errors // CHECK-NEXT:| `-UnresolvedLookupExpr {{.*}} 'invalid' Index: clang/lib/Sema/SemaOverload.cpp === --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -14080,14 +14080,17 @@ UnbridgedCasts.restore(); OverloadCandidateSet::iterator Best; +bool Succeeded = true; switch (CandidateSet.BestViableFunction(*this, UnresExpr->getBeginLoc(), Best)) { case OR_Success: Method = cast(Best->Function); FoundDecl = Best->FoundDecl; CheckUnresolvedMemberAccess(UnresExpr, Best->FoundDecl); - if (DiagnoseUseOfDecl(Best->FoundDecl, UnresExpr->getNameLoc())) -return ExprError(); + if (DiagnoseUseOfDecl(Best->FoundDecl, UnresExpr->getNameLoc())) { +Succeeded = false; +break; + } // If FoundDecl is different from Method (such as if one is a template // and the other a specialization), make sure DiagnoseUseOfDecl is // called on both. @@ -14096,7 +14099,7 @@ // being used. if (Method != FoundDecl.getDecl() && DiagnoseUseOfDecl(Method, UnresExpr->getNameLoc())) -return ExprError(); +Succeeded = false; break; case OR_No_Viable_Function: @@ -14106,8 +14109,8 @@ PDiag(diag::err_ovl_no_viable_member_function_in_call) << DeclName << MemExprE->getSourceRange()), *this, OCD_AllCandidates, Args); - // FIXME: Leaking incoming expressions! - return ExprError(); + Succeeded = false; + break; case OR_Ambiguous: CandidateSet.NoteCandidates( @@ -14115,8 +14118,8 @@ PDiag(diag::err_ovl_ambiguous_member_call) << DeclName << MemExprE->getSourceRange()), *this, OCD_AmbiguousCandidates, Args); - // FIXME: Leaking incoming expressions! - return ExprError(); + Succeeded = false; + break; case OR_Deleted: CandidateSet.NoteCandidates( @@ -14124,9 +14127,14 @@ PDiag(diag::err_ovl_deleted_member_call) << DeclName << MemExprE->getSourceRange()), *this, OCD_AllCandidates, Args); - // FIXME: Leaking incoming expressions! - return ExprError(); + Succeeded = false; + break; } +// Overload resolvation fails, try to recover. +if (!Succeeded) + return CreateRecoveryExpr(MemExprE->getBeginLoc(), MemExprE->getEndLoc(), +{MemExprE}, +chooseRecoveryType(CandidateSet, )); MemExprE = FixOverloadedFunctionReference(MemExprE, FoundDecl, Method); ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits