lichray created this revision.
Herald added a subscriber: JDevlieghere.
lichray requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

As a Clang extension. See also https://wg21.link/p0849r2

The implementation takes a shortcut by forming CXXFunctionalCastExpr.
Doing so costs losing 'decltype(auto)' keyword in AST. Although
we do that elsewhere, because this is in an expression, it's more
difficult to keep AST print legal *and* trustworthy.

Depends on D113393 <https://reviews.llvm.org/D113393>


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D120589

Files:
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaExprCXX.cpp
  clang/lib/Sema/SemaType.cpp
  clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.auto.deduct/p2.cpp
  clang/test/CXX/expr/expr.post/expr.type.conv/p1-2b.cpp
  clang/test/SemaCXX/deduced-return-type-cxx14.cpp

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
@@ -442,6 +442,15 @@
       B() : decltype(auto)() {} // expected-error {{'decltype(auto)' not allowed here}}
     };
   }
+
+  namespace Cast {
+    void foo() {
+      (void)decltype(auto)(0); // cxx14_20-error{{'decltype(auto)' not allowed here}} \
+                                  cxx2b-warning{{functional-style cast to 'decltype(auto)' is a Clang extension}}
+      (void)decltype(auto){0}; // cxx14_20-error{{'decltype(auto)' not allowed here}} \
+                                  cxx2b-warning{{functional-style cast to 'decltype(auto)' is a Clang extension}}
+    }
+  }
 }
 
 namespace CurrentInstantiation {
Index: clang/test/CXX/expr/expr.post/expr.type.conv/p1-2b.cpp
===================================================================
--- clang/test/CXX/expr/expr.post/expr.type.conv/p1-2b.cpp
+++ clang/test/CXX/expr/expr.post/expr.type.conv/p1-2b.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++2b -verify %s
+// RUN: %clang_cc1 -std=c++2b -Wno-decltype-auto-cast -verify %s
 
 template <class T>
 void foo(T);
@@ -37,3 +37,26 @@
   foo(auto({1, 2})); // expected-error {{cannot deduce actual type for 'auto' from parenthesized initializer list}}
   foo(auto{{1, 2}}); // expected-error {{cannot deduce actual type for 'auto' from nested initializer list}}
 }
+
+void diagnostics_extension() {
+  foo(decltype(auto)());   // expected-error {{initializer for functional-style cast to 'decltype(auto)' is empty}}
+  foo(decltype(auto){});   // expected-error {{initializer for functional-style cast to 'decltype(auto)' is empty}}
+  foo(decltype(auto)({})); // expected-error {{cannot deduce actual type for 'decltype(auto)' from parenthesized initializer list}}
+  foo(decltype(auto){{}}); // expected-error {{cannot deduce actual type for 'decltype(auto)' from nested initializer list}}
+
+  foo(decltype(auto)({a})); // expected-error {{cannot deduce actual type for 'decltype(auto)' from parenthesized initializer list}}
+  foo(decltype(auto){{a}}); // expected-error {{cannot deduce actual type for 'decltype(auto)' from nested initializer list}}
+
+  foo(decltype(auto)(&A::g)); // expected-error {{reference to overloaded function could not be resolved}}
+
+  foo(decltype(auto)(a, 3.14));     // expected-error {{initializer for functional-style cast to 'decltype(auto)' contains multiple expressions}}
+  foo(decltype(auto){a, 3.14});     // expected-error {{initializer for functional-style cast to 'decltype(auto)' contains multiple expressions}}
+  foo(decltype(auto)({a, 3.14}));   // expected-error {{cannot deduce actual type for 'decltype(auto)' from parenthesized initializer list}}
+  foo(decltype(auto){{a, 3.14}});   // expected-error {{cannot deduce actual type for 'decltype(auto)' from nested initializer list}}
+  foo(decltype(auto)({a}, {3.14})); // expected-error {{initializer for functional-style cast to 'decltype(auto)' contains multiple expressions}}
+  foo(decltype(auto){{a}, {3.14}}); // expected-error {{initializer for functional-style cast to 'decltype(auto)' contains multiple expressions}}
+
+  foo(decltype(auto){1, 2});   // expected-error {{initializer for functional-style cast to 'decltype(auto)' contains multiple expressions}}
+  foo(decltype(auto)({1, 2})); // expected-error {{cannot deduce actual type for 'decltype(auto)' from parenthesized initializer list}}
+  foo(decltype(auto){{1, 2}}); // expected-error {{cannot deduce actual type for 'decltype(auto)' from nested initializer list}}
+}
Index: clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.auto.deduct/p2.cpp
===================================================================
--- clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.auto.deduct/p2.cpp
+++ clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.auto.deduct/p2.cpp
@@ -1,6 +1,7 @@
-// RUN: %clang_cc1 -std=c++2b -verify %s
+// RUN: %clang_cc1 -std=c++2b -Wno-decltype-auto-cast -verify %s
 
 // p2.3 allows only T = auto in T(x).
+// As a Clang extension, we also allow T = decltype(auto) to match p2.2 (new T(x)).
 
 void test_decay() {
   int v[3];
@@ -9,6 +10,18 @@
   static_assert(__is_same(decltype(auto("lit")), char const *));
   static_assert(__is_same(decltype(auto{"lit"}), char const *));
 
+  void(decltype(auto)(v)); // expected-error {{functional-style cast}}
+  void(decltype(auto){v}); // expected-error {{cannot initialize an array element}}
+  static_assert(__is_same(decltype(decltype(auto)("lit")), char const(&)[4]));
+  static_assert(__is_same(decltype(decltype(auto){"lit"}), char const(&)[4]));
+
+  int fn(char *);
+  static_assert(__is_same(decltype(auto(fn)), int (*)(char *)));
+  static_assert(__is_same(decltype(auto{fn}), int (*)(char *)));
+
+  void(decltype(auto)(fn)); // expected-error{{functional-style cast}}
+  void(decltype(auto){fn}); // expected-error{{cannot create object of function type}}
+
   constexpr long i = 1;
   static_assert(__is_same(decltype(i), long const));
   static_assert(__is_same(decltype(auto(1L)), long));
@@ -16,6 +29,12 @@
   static_assert(__is_same(decltype(auto(i)), long));
   static_assert(__is_same(decltype(auto{i}), long));
 
+  // scalar prvalue is not cv-qualified
+  static_assert(__is_same(decltype(decltype(auto)(1L)), long));
+  static_assert(__is_same(decltype(decltype(auto){1L}), long));
+  static_assert(__is_same(decltype(decltype(auto)(i)), long));
+  static_assert(__is_same(decltype(decltype(auto){i}), long));
+
   class A {
   } a;
   A const ac;
@@ -23,6 +42,18 @@
   static_assert(__is_same(decltype(auto(a)), A));
   static_assert(__is_same(decltype(auto(ac)), A));
 
+  static_assert(__is_same(decltype(decltype(auto)(a)), A));
+  static_assert(__is_same(decltype(decltype(auto)(ac)), A const));
+
+  static_assert(__is_same(decltype(decltype(auto)((a))), A &));
+  static_assert(__is_same(decltype(decltype(auto)((ac))), A const &));
+
+  static_assert(__is_same(decltype(decltype(auto){a}), A));
+  static_assert(__is_same(decltype(decltype(auto){ac}), A const));
+
+  static_assert(__is_same(decltype(decltype(auto){(a)}), A &));
+  static_assert(__is_same(decltype(decltype(auto){(ac)}), A const &));
+
   A &lr = a;
   A const &lrc = a;
   A &&rr = static_cast<A &&>(a);
@@ -32,6 +63,11 @@
   static_assert(__is_same(decltype(auto(lrc)), A));
   static_assert(__is_same(decltype(auto(rr)), A));
   static_assert(__is_same(decltype(auto(rrc)), A));
+
+  static_assert(__is_same(decltype(decltype(auto)(lr)), A &));
+  static_assert(__is_same(decltype(decltype(auto)(lrc)), A const &));
+  static_assert(__is_same(decltype(decltype(auto)(rr)), A &&));
+  static_assert(__is_same(decltype(decltype(auto)(rrc)), A const &&));
 }
 
 class cmdline_parser {
@@ -79,3 +115,71 @@
   constexpr Uncopyable(Uncopyable &&) = delete;
 } u = auto(Uncopyable(auto(Uncopyable(42))));
 } // namespace auto_x
+
+// decltype(auto) is no-op to prvalues
+namespace decltype_auto_x {
+constexpr struct Uncopyable {
+  constexpr explicit Uncopyable(int) {}
+  constexpr Uncopyable(Uncopyable &&) = delete;
+} u = decltype(auto)(Uncopyable(decltype(auto)(Uncopyable(42))));
+} // namespace decltype_auto_x
+
+// Forward with decltype(auto)
+constexpr auto invoke1 = [](auto &&x, auto &&y) {
+  return decltype(auto)(x)(decltype(auto)(y));
+};
+
+struct MoveOnly {
+  MoveOnly() = default;
+  MoveOnly(MoveOnly &&) = default;
+  MoveOnly(MoveOnly const &) = delete;
+};
+
+constexpr MoveOnly getMoveOnly() { return {}; }
+
+struct Fn {
+  constexpr int operator()(MoveOnly &) & { return 0; }
+  constexpr int operator()(MoveOnly &&) & { return 1; }
+
+  constexpr int operator()(MoveOnly &) && { return 2; }
+  constexpr int operator()(MoveOnly &&) && { return 3; }
+
+  constexpr int operator()(MoveOnly &) const & { return 4; }
+  constexpr int operator()(MoveOnly &&) const & { return 5; }
+
+  constexpr int operator()(MoveOnly &) const && { return 6; }
+  constexpr int operator()(MoveOnly &&) const && { return 7; }
+};
+
+constexpr void FwdWithDecltypeAuto() {
+  MoveOnly lv;
+  Fn f;
+  constexpr Fn cf;
+
+  static_assert(invoke1(f, lv) == 0);
+  static_assert(invoke1(f, getMoveOnly()) == 1);
+
+  static_assert(invoke1(Fn{}, lv) == 2);
+  static_assert(invoke1(Fn{}, getMoveOnly()) == 3);
+
+  static_assert(invoke1(cf, lv) == 4);
+  static_assert(invoke1(cf, getMoveOnly()) == 5);
+
+  static_assert(invoke1((Fn const){}, lv) == 6);
+  static_assert(invoke1((Fn const){}, getMoveOnly()) == 7);
+}
+
+struct FnArray {
+  template <class T, int N>
+  constexpr int operator()(T (&)[N]) const { return 0; }
+
+  template <class T, int N>
+  constexpr int operator()(T (&&)[N]) const { return 1; }
+};
+
+constexpr void FwdArrayWithDecltypeAuto() {
+  FnArray f;
+
+  static_assert(invoke1(f, "foo") == 0);
+  static_assert(invoke1(f, (int[]){1, 2, 3}) == 1);
+}
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ clang/lib/Sema/SemaType.cpp
@@ -3509,9 +3509,8 @@
     case DeclaratorContext::FunctionalCast:
       if (isa<DeducedTemplateSpecializationType>(Deduced))
         break;
-      if (SemaRef.getLangOpts().CPlusPlus2b && IsCXXAutoType &&
-          !Auto->isDecltypeAuto())
-        break; // auto(x)
+      if (SemaRef.getLangOpts().CPlusPlus2b && IsCXXAutoType)
+        break; // auto(x) and decltype(auto)(x)
       LLVM_FALLTHROUGH;
     case DeclaratorContext::TypeName:
       Error = 15; // Generic
Index: clang/lib/Sema/SemaExprCXX.cpp
===================================================================
--- clang/lib/Sema/SemaExprCXX.cpp
+++ clang/lib/Sema/SemaExprCXX.cpp
@@ -1495,8 +1495,11 @@
                        << Ty << FullRange);
     }
     if (getLangOpts().CPlusPlus2b) {
-      if (Ty->getAs<AutoType>())
-        Diag(TyBeginLoc, diag::warn_cxx20_compat_auto_expr) << FullRange;
+      if (auto *TyAuto = Ty->getAs<AutoType>())
+        Diag(TyBeginLoc, TyAuto->isDecltypeAuto()
+                             ? diag::ext_decltype_auto_expr
+                             : diag::warn_cxx20_compat_auto_expr)
+            << FullRange;
     }
     Expr *Deduce = Inits[0];
     if (isa<InitListExpr>(Deduce))
@@ -1512,6 +1515,14 @@
       return ExprError();
 
     Ty = DeducedType;
+    if (Ty->isReferenceType()) {
+      // decltype(auto)(x) takes a shortcut; see also P0849R2.
+      // FIXME: Substitute auto here to prevent a crash when diagnosing lifetime
+      // of array argument in constant evaluation. Shouldn't be done this way.
+      return BuildCXXFunctionalCastExpr(SubstAutoTypeSourceInfo(TInfo, Ty), Ty,
+                                        LParenOrBraceLoc, Deduce,
+                                        RParenOrBraceLoc);
+    }
     Entity = InitializedEntity::InitializeTemporary(TInfo, Ty);
   }
 
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2387,6 +2387,9 @@
   "cannot form %select{pointer to|reference to|array of}0 'decltype(auto)'">;
 def err_decltype_auto_initializer_list : Error<
   "cannot deduce 'decltype(auto)' from initializer list">;
+def ext_decltype_auto_expr : ExtWarn<
+  "functional-style cast to 'decltype(auto)' is a Clang extension">,
+  InGroup<DiagGroup<"decltype-auto-cast">>;
 
 // C++17 deduced class template specialization types
 def err_deduced_class_template_compound_type : Error<
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to