hokein created this revision.
hokein added a reviewer: sammccall.
Herald added a project: clang.

RecoveryExpr was always lvalue, but it is wrong if we use it to model
broken function calls, function call expression has more compliated rules:

- a call to a function whose return type is an lvalue reference yields an 
lvalue;
- a call to a function whose return type is an rvalue reference yields an 
xvalue;
- a call to a function whose return type is nonreference type yields a prvalue;

This patch makes the recovery-expr align with the function call if it is
modeled a broken call.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D83201

Files:
  clang/include/clang/AST/Expr.h
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/Expr.cpp
  clang/lib/AST/ExprClassification.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/lib/Sema/SemaOverload.cpp
  clang/test/AST/ast-dump-recovery.cpp
  clang/test/SemaCXX/recovery-expr-type.cpp

Index: clang/test/SemaCXX/recovery-expr-type.cpp
===================================================================
--- clang/test/SemaCXX/recovery-expr-type.cpp
+++ clang/test/SemaCXX/recovery-expr-type.cpp
@@ -62,3 +62,9 @@
                                      // expected-note {{in instantiation of member function}} \
                                      // expected-note {{in call to}}
 }
+
+// verify no assertion failure on violating value category.
+namespace test4 {
+int&&f(int); // expected-note {{candidate function not viable}}
+int&& k = f(); // expected-error {{no matching function for call}}
+}
Index: clang/test/AST/ast-dump-recovery.cpp
===================================================================
--- clang/test/AST/ast-dump-recovery.cpp
+++ clang/test/AST/ast-dump-recovery.cpp
@@ -4,7 +4,6 @@
 int some_func(int *);
 
 // CHECK:     VarDecl {{.*}} invalid_call
-// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int' contains-errors
 // CHECK-NEXT:  `-RecoveryExpr {{.*}} 'int' contains-errors
 // CHECK-NEXT:    |-UnresolvedLookupExpr {{.*}} 'some_func'
 // CHECK-NEXT:    `-IntegerLiteral {{.*}} 123
@@ -34,7 +33,6 @@
 int ambig_func(float);
 
 // CHECK:     VarDecl {{.*}} ambig_call
-// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int' contains-errors
 // CHECK-NEXT:  `-RecoveryExpr {{.*}} 'int' contains-errors
 // CHECK-NEXT:    |-UnresolvedLookupExpr {{.*}} 'ambig_func'
 // CHECK-NEXT:    `-IntegerLiteral {{.*}} 123
@@ -211,3 +209,16 @@
 } NoCrashOnInvalidInitList = {
   .abc = nullptr,
 };
+
+// Verify the value category of recovery expression.
+int f1(int);
+int& f2(int);
+int&& f3(int);
+void ValueCategory() {
+  // CHECK:  RecoveryExpr {{.*}} 'int' contains-errors
+  f1(); // call to a function (nonreference return type) yields a prvalue (not print by default)
+  // CHECK:  RecoveryExpr {{.*}} 'int' contains-errors lvalue
+  f2(); // call to a function (lvalue reference return type) yields an lvalue.
+  // CHECK:  RecoveryExpr {{.*}} 'int' contains-errors xvalue
+  f3(); // call to a function (rvalue reference return type) yields an xvalue.
+}
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -12818,7 +12818,7 @@
   auto ConsiderCandidate = [&](const OverloadCandidate &Candidate) {
     if (!Candidate.Function)
       return;
-    QualType T = Candidate.Function->getCallResultType();
+    QualType T = Candidate.Function->getReturnType();
     if (T.isNull())
       return;
     if (!Result)
@@ -12938,8 +12938,15 @@
   // Overload resolution failed, try to recover.
   SmallVector<Expr *, 8> SubExprs = {Fn};
   SubExprs.append(Args.begin(), Args.end());
-  return SemaRef.CreateRecoveryExpr(Fn->getBeginLoc(), RParenLoc, SubExprs,
-                                    chooseRecoveryType(*CandidateSet, Best));
+  auto ReturnType = chooseRecoveryType(*CandidateSet, Best);
+  auto Recovery = SemaRef.CreateRecoveryExpr(
+      Fn->getBeginLoc(), RParenLoc, SubExprs,
+      ReturnType.isNull()
+          ? ReturnType
+          // set the call result type.
+          : ReturnType.getNonLValueExprType(SemaRef.getASTContext()),
+      ReturnType.isNull() ? VK_LValue : Expr::getValueKindForType(ReturnType));
+  return Recovery;
 }
 
 static void markUnaddressableCandidatesUnviable(Sema &S,
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -19209,7 +19209,8 @@
 }
 
 ExprResult Sema::CreateRecoveryExpr(SourceLocation Begin, SourceLocation End,
-                                    ArrayRef<Expr *> SubExprs, QualType T) {
+                                    ArrayRef<Expr *> SubExprs, QualType T,
+                                    ExprValueKind VK) {
   // FIXME: enable it for C++, RecoveryExpr is type-dependent to suppress
   // bogus diagnostics and this trick does not work in C.
   // FIXME: use containsErrors() to suppress unwanted diags in C.
@@ -19219,8 +19220,10 @@
   if (isSFINAEContext())
     return ExprError();
 
-  if (T.isNull() || !Context.getLangOpts().RecoveryASTType)
+  if (T.isNull() || !Context.getLangOpts().RecoveryASTType) {
     // We don't know the concrete type, fallback to dependent type.
     T = Context.DependentTy;
-  return RecoveryExpr::Create(Context, T, Begin, End, SubExprs);
+    VK = VK_LValue;
+  }
+  return RecoveryExpr::Create(Context, T, VK, Begin, End, SubExprs);
 }
Index: clang/lib/AST/ExprClassification.cpp
===================================================================
--- clang/lib/AST/ExprClassification.cpp
+++ clang/lib/AST/ExprClassification.cpp
@@ -130,7 +130,6 @@
   case Expr::UnresolvedLookupExprClass:
   case Expr::UnresolvedMemberExprClass:
   case Expr::TypoExprClass:
-  case Expr::RecoveryExprClass:
   case Expr::DependentCoawaitExprClass:
   case Expr::CXXDependentScopeMemberExprClass:
   case Expr::DependentScopeDeclRefExprClass:
@@ -145,6 +144,9 @@
   case Expr::OMPIteratorExprClass:
     return Cl::CL_LValue;
 
+  case Expr::RecoveryExprClass:
+    return ClassifyExprValueKind(Lang, E, E->getValueKind());
+
     // C99 6.5.2.5p5 says that compound literals are lvalues.
     // In C++, they're prvalue temporaries, except for file-scope arrays.
   case Expr::CompoundLiteralExprClass:
Index: clang/lib/AST/Expr.cpp
===================================================================
--- clang/lib/AST/Expr.cpp
+++ clang/lib/AST/Expr.cpp
@@ -4777,9 +4777,10 @@
   return OriginalTy;
 }
 
-RecoveryExpr::RecoveryExpr(ASTContext &Ctx, QualType T, SourceLocation BeginLoc,
-                           SourceLocation EndLoc, ArrayRef<Expr *> SubExprs)
-    : Expr(RecoveryExprClass, T, VK_LValue, OK_Ordinary), BeginLoc(BeginLoc),
+RecoveryExpr::RecoveryExpr(ASTContext &Ctx, QualType T, ExprValueKind VK,
+                           SourceLocation BeginLoc, SourceLocation EndLoc,
+                           ArrayRef<Expr *> SubExprs)
+    : Expr(RecoveryExprClass, T, VK, OK_Ordinary), BeginLoc(BeginLoc),
       EndLoc(EndLoc), NumExprs(SubExprs.size()) {
   assert(!T.isNull());
   assert(llvm::all_of(SubExprs, [](Expr* E) { return E != nullptr; }));
@@ -4789,12 +4790,12 @@
 }
 
 RecoveryExpr *RecoveryExpr::Create(ASTContext &Ctx, QualType T,
-                                   SourceLocation BeginLoc,
+                                   ExprValueKind VK, SourceLocation BeginLoc,
                                    SourceLocation EndLoc,
                                    ArrayRef<Expr *> SubExprs) {
   void *Mem = Ctx.Allocate(totalSizeToAlloc<Expr *>(SubExprs.size()),
                            alignof(RecoveryExpr));
-  return new (Mem) RecoveryExpr(Ctx, T, BeginLoc, EndLoc, SubExprs);
+  return new (Mem) RecoveryExpr(Ctx, T, VK, BeginLoc, EndLoc, SubExprs);
 }
 
 RecoveryExpr *RecoveryExpr::CreateEmpty(ASTContext &Ctx, unsigned NumSubExprs) {
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -3952,7 +3952,8 @@
   /// Attempts to produce a RecoveryExpr after some AST node cannot be created.
   ExprResult CreateRecoveryExpr(SourceLocation Begin, SourceLocation End,
                                 ArrayRef<Expr *> SubExprs,
-                                QualType T = QualType());
+                                QualType T = QualType(),
+                                ExprValueKind VK = VK_LValue);
 
   ObjCInterfaceDecl *getObjCInterfaceDecl(IdentifierInfo *&Id,
                                           SourceLocation IdLoc,
Index: clang/include/clang/AST/Expr.h
===================================================================
--- clang/include/clang/AST/Expr.h
+++ clang/include/clang/AST/Expr.h
@@ -6228,7 +6228,7 @@
 class RecoveryExpr final : public Expr,
                            private llvm::TrailingObjects<RecoveryExpr, Expr *> {
 public:
-  static RecoveryExpr *Create(ASTContext &Ctx, QualType T,
+  static RecoveryExpr *Create(ASTContext &Ctx, QualType T, ExprValueKind VK,
                               SourceLocation BeginLoc, SourceLocation EndLoc,
                               ArrayRef<Expr *> SubExprs);
   static RecoveryExpr *CreateEmpty(ASTContext &Ctx, unsigned NumSubExprs);
@@ -6255,8 +6255,9 @@
   }
 
 private:
-  RecoveryExpr(ASTContext &Ctx, QualType T, SourceLocation BeginLoc,
-               SourceLocation EndLoc, ArrayRef<Expr *> SubExprs);
+  RecoveryExpr(ASTContext &Ctx, QualType T, ExprValueKind VK,
+               SourceLocation BeginLoc, SourceLocation EndLoc,
+               ArrayRef<Expr *> SubExprs);
   RecoveryExpr(EmptyShell Empty, unsigned NumSubExprs)
       : Expr(RecoveryExprClass, Empty), NumExprs(NumSubExprs) {}
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to