[clang] f0891cd - [clang] [concepts] Check constrained-auto return types for void-returning functions

Arthur O'Dwyer via cfe-commits cfe-commits at lists.llvm.org
Fri Mar 4 09:43:29 PST 2022


Author: Arthur O'Dwyer
Date: 2022-03-04T12:43:06-05:00
New Revision: f0891cd61b2f7cd57d906406ae785722bfd87603

URL: https://github.com/llvm/llvm-project/commit/f0891cd61b2f7cd57d906406ae785722bfd87603
DIFF: https://github.com/llvm/llvm-project/commit/f0891cd61b2f7cd57d906406ae785722bfd87603.diff

LOG: [clang] [concepts] Check constrained-auto return types for void-returning functions

Fixes #49188.

Differential Revision: https://reviews.llvm.org/D119184

Added: 
    

Modified: 
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaStmt.cpp
    clang/test/SemaTemplate/concepts.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 7d9d5ec87948e..b88d9f2f847fd 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -14674,18 +14674,20 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
       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'.
+          // For a function with a deduced result type to return void,
+          // the result type as written must be 'auto' or 'decltype(auto)',
+          // possibly cv-qualified or constrained, but not ref-qualified.
           if (!FD->getReturnType()->getAs<AutoType>()) {
             Diag(dcl->getLocation(), diag::err_auto_fn_no_return_but_not_auto)
                 << FD->getReturnType();
             FD->setInvalidDecl();
           } else {
-            // Substitute 'void' for the 'auto' in the type.
-            TypeLoc ResultType = getReturnTypeLoc(FD);
-            Context.adjustDeducedFunctionResultType(
-                FD, SubstAutoType(ResultType.getType(), Context.VoidTy));
+            // 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>()))
+              FD->setInvalidDecl();
           }
         }
       } else if (getLangOpts().CPlusPlus11 && isLambdaCallOperator(FD)) {

diff  --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index d51a71ffc7f41..e23fd23019998 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -3808,19 +3808,26 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD,
     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.
+    // For a function with a deduced result type to return void,
+    // the result type as written must be 'auto' or 'decltype(auto)',
+    // possibly cv-qualified or constrained, but not ref-qualified.
     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()'.
+    Expr *Dummy = new (Context) CXXScalarValueInitExpr(
+        Context.VoidTy,
+        Context.getTrivialTypeSourceInfo(Context.VoidTy, ReturnLoc), ReturnLoc);
+    DeduceAutoResult DAR = DeduceAutoType(OrigResultType, Dummy, Deduced);
+
+    if (DAR == DAR_Failed && !FD->isInvalidDecl())
+      Diag(ReturnLoc, diag::err_auto_fn_deduction_failure)
+          << OrigResultType.getType() << Dummy->getType();
+
+    if (DAR != DAR_Succeeded)
       return true;
   }
 

diff  --git a/clang/test/SemaTemplate/concepts.cpp b/clang/test/SemaTemplate/concepts.cpp
index c297a75dd82e6..20af504883566 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -169,3 +169,42 @@ namespace PR50561 {
   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() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
+    return void();
+  }
+  C auto f2() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
+    return;
+  }
+  C auto f3() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
+  }
+  C decltype(auto) f4() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
+    return void();
+  }
+  C decltype(auto) f5() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
+    return;
+  }
+  C decltype(auto) f6() { // expected-error {{deduced type 'void' does not satisfy 'C'}}
+  }
+  C auto& f7() { // expected-error {{cannot form a reference to 'void'}}
+    return void();
+  }
+  C auto& f8() {
+    return; // expected-error {{cannot deduce return type 'C auto &' from omitted return expression}}
+  }
+  C auto& f9() { // expected-error {{cannot deduce return type 'C auto &' for function with no return statements}}
+  }
+}
+namespace PR53911 {
+  template<class T> concept C = false;
+
+  C auto *f1() {
+    return (void*)nullptr; // FIXME: should error
+  }
+  C auto *f2() {
+    return (int*)nullptr; // FIXME: should error
+  }
+}


        


More information about the cfe-commits mailing list