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