Quuxplusone updated this revision to Diff 406847.
Quuxplusone added a comment.

Update one additional test where the expected output has changed:

  auto& f() { }

now produces "can't make a reference to void" instead of "can't deduce `auto&` 
from no return statements." I think this is a mild regression in diagnostic 
quality, but I don't immediately see what we should do about it so I'm inclined 
to take the hit. Suggestions welcome.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D119184/new/

https://reviews.llvm.org/D119184

Files:
  clang/include/clang/Sema/Sema.h
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaStmt.cpp
  clang/test/SemaCXX/deduced-return-type-cxx14.cpp
  clang/test/SemaTemplate/concepts.cpp

Index: clang/test/SemaTemplate/concepts.cpp
===================================================================
--- clang/test/SemaTemplate/concepts.cpp
+++ clang/test/SemaTemplate/concepts.cpp
@@ -169,3 +169,14 @@
   template<C T, typename U> void f(T, U) = delete;
   void g() { f(0, 0); }
 }
+
+namespace PR49188 {
+  template<class T> concept C = false; // expected-note 6 {{because 'false' evaluated to false}}
+  C auto f1() { return void(); } // expected-error {{deduced type 'void' does not satisfy 'C'}}
+  C auto f2() { return; } // expected-error {{deduced type 'void' does not satisfy 'C'}}
+  C auto f3() { } // expected-error {{deduced type 'void' does not satisfy 'C'}}
+  C decltype(auto) f4() { return void(); } // expected-error {{deduced type 'void' does not satisfy 'C'}}
+  C decltype(auto) f5() { return; } // expected-error {{deduced type 'void' does not satisfy 'C'}}
+  C decltype(auto) f6() { } // expected-error {{deduced type 'void' does not satisfy 'C'}}
+  void g() { f1(); f2(); f3(); f4(); f5(); f6(); }
+}
Index: clang/test/SemaCXX/deduced-return-type-cxx14.cpp
===================================================================
--- clang/test/SemaCXX/deduced-return-type-cxx14.cpp
+++ clang/test/SemaCXX/deduced-return-type-cxx14.cpp
@@ -113,7 +113,7 @@
 using Void = void;
 using Void = decltype(void_ret());
 
-auto &void_ret_2() {} // expected-error {{cannot deduce return type 'auto &' for function with no return statements}}
+auto &void_ret_2() {} // expected-error {{cannot form a reference to 'void'}}
 const auto void_ret_3() {} // ok, return type 'const void' is adjusted to 'void'
 
 const auto void_ret_4() {
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -3590,7 +3590,7 @@
 
     AutoType *AT = CurCap->ReturnType->getContainedAutoType();
     assert(AT && "lost auto type from lambda return type");
-    if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) {
+    if (DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT, true)) {
       FD->setInvalidDecl();
       // FIXME: preserve the ill-formed return expression.
       return StmtError();
@@ -3762,8 +3762,9 @@
 bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD,
                                             SourceLocation ReturnLoc,
                                             Expr *&RetExpr,
-                                            AutoType *AT) {
-  // If this is the conversion function for a lambda, we choose to deduce it
+                                            const AutoType *AT,
+                                            bool HasReturnStmt) {
+  // If this is the conversion function for a lambda, we choose to deduce its
   // type from the corresponding call operator, not from the synthesized return
   // statement within it. See Sema::DeduceReturnType.
   if (isLambdaConversionOperator(FD))
@@ -3808,19 +3809,18 @@
     LocalTypedefNameReferencer Referencer(*this);
     Referencer.TraverseType(RetExpr->getType());
   } else {
-    //  In the case of a return with no operand, the initializer is considered
-    //  to be void().
-    //
-    // Deduction here can only succeed if the return type is exactly 'cv auto'
-    // or 'decltype(auto)', so just check for that case directly.
-    if (!OrigResultType.getType()->getAs<AutoType>()) {
-      Diag(ReturnLoc, diag::err_auto_fn_return_void_but_not_auto)
-        << OrigResultType.getType();
-      return true;
-    }
-    // We always deduce U = void in this case.
-    Deduced = SubstAutoType(OrigResultType.getType(), Context.VoidTy);
-    if (Deduced.isNull())
+    // In the case of a return with no operand, the initializer is considered
+    // to be 'void()'.
+    ExprResult R = new (Context) CXXScalarValueInitExpr(
+      Context.VoidTy, Context.getTrivialTypeSourceInfo(Context.VoidTy, ReturnLoc),
+      ReturnLoc);
+    Expr *Dummy = R.get();
+    DeduceAutoResult DAR = DeduceAutoType(OrigResultType, Dummy, Deduced);
+    if (DAR == DAR_Failed && !FD->isInvalidDecl())
+      Diag(ReturnLoc, HasReturnStmt ? diag::err_auto_fn_return_void_but_not_auto
+                                    : diag::err_auto_fn_no_return_but_not_auto)
+          << OrigResultType.getType();
+    if (DAR != DAR_Succeeded)
       return true;
   }
 
@@ -3989,7 +3989,7 @@
       // trying to deduce its return type.
       // (Some return values may be needlessly wrapped in RecoveryExpr).
       if (FD->isInvalidDecl() ||
-          DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT)) {
+          DeduceFunctionTypeFromReturnExpr(FD, ReturnLoc, RetValExp, AT, true)) {
         FD->setInvalidDecl();
         if (!AllowRecovery)
           return StmtError();
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -14660,18 +14660,12 @@
       if (getLangOpts().CPlusPlus14) {
         if (!FD->isInvalidDecl() && Body && !FD->isDependentContext() &&
             FD->getReturnType()->isUndeducedType()) {
-          // If the function has a deduced result type but contains no 'return'
-          // statements, the result type as written must be exactly 'auto', and
-          // the deduced result type is 'void'.
-          if (!FD->getReturnType()->getAs<AutoType>()) {
-            Diag(dcl->getLocation(), diag::err_auto_fn_no_return_but_not_auto)
-                << FD->getReturnType();
+          // Falling off the end of the function is the same as 'return;'.
+          Expr *Dummy = nullptr;
+          if (DeduceFunctionTypeFromReturnExpr(
+                  FD, dcl->getLocation(), Dummy,
+                  FD->getReturnType()->getAs<AutoType>(), false)) {
             FD->setInvalidDecl();
-          } else {
-            // Substitute 'void' for the 'auto' in the type.
-            TypeLoc ResultType = getReturnTypeLoc(FD);
-            Context.adjustDeducedFunctionResultType(
-                FD, SubstAutoType(ResultType.getType(), Context.VoidTy));
           }
         }
       } else if (getLangOpts().CPlusPlus11 && isLambdaCallOperator(FD)) {
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -8657,7 +8657,8 @@
 
   bool DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD,
                                         SourceLocation ReturnLoc,
-                                        Expr *&RetExpr, AutoType *AT);
+                                        Expr *&RetExpr, const AutoType *AT,
+                                        bool HasReturnStmt);
 
   FunctionTemplateDecl *getMoreSpecializedTemplate(
       FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to