diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2320,8 +2320,12 @@ "type %0 to use list-initialization">, InGroup; def err_auto_var_init_no_expression : Error< "initializer for variable %0 with type %1 is empty">; +def err_auto_expr_init_no_expression : Error< + "initializer for functional-style cast to %0 is empty">; def err_auto_var_init_multiple_expressions : Error< "initializer for variable %0 with type %1 contains multiple expressions">; +def err_auto_expr_init_multiple_expressions : Error< + "initializer for functional-style cast to %0 contains multiple expressions">; def err_auto_var_init_paren_braces : Error< "cannot deduce type for variable %1 with type %2 from " "%select{parenthesized|nested}0 initializer list">; @@ -2340,6 +2344,8 @@ "variable %0 with type %1 has incompatible initializer of type %2">; def err_auto_var_deduction_failure_from_init_list : Error< "cannot deduce actual type for variable %0 with type %1 from initializer list">; +def err_auto_expr_deduction_failure : Error< + "functional-style cast to %0 has incompatible initializer of type %1">; def err_auto_new_deduction_failure : Error< "new expression for type %0 has incompatible constructor argument of type %1">; def err_auto_inconsistent_deduction : Error< diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1027,10 +1027,9 @@ } // Check for C++1y 'decltype(auto)'. - if (Tok.is(tok::kw_auto)) { - // No need to disambiguate here: an expression can't start with 'auto', - // because the typename-specifier in a function-style cast operation can't - // be 'auto'. + if (Tok.is(tok::kw_auto) && NextToken().is(tok::r_paren)) { + // the typename-specifier in a function-style cast expression may + // be 'auto' since C++2y Diag(Tok.getLocation(), getLangOpts().CPlusPlus14 ? diag::warn_cxx11_compat_decltype_auto_type_specifier diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1523,6 +1523,7 @@ case tok::kw___float128: case tok::kw___ibm128: case tok::kw_void: + case tok::kw_auto: case tok::kw_typename: case tok::kw_typeof: case tok::kw___vector: diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -2226,6 +2226,9 @@ case tok::kw_void: DS.SetTypeSpecType(DeclSpec::TST_void, Loc, PrevSpec, DiagID, Policy); break; + case tok::kw_auto: + DS.SetTypeSpecType(DeclSpec::TST_auto, Loc, PrevSpec, DiagID, Policy); + break; case tok::kw_char: DS.SetTypeSpecType(DeclSpec::TST_char, Loc, PrevSpec, DiagID, Policy); break; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1467,6 +1467,9 @@ // C++1z [expr.type.conv]p1: // If the type is a placeholder for a deduced class type, [...perform class // template argument deduction...] + // C++2y: + // Otherwise, if the type contains a placeholder type, it is replaced by the + // type determined by placeholder type deduction. DeducedType *Deduced = Ty->getContainedDeducedType(); if (Deduced && isa(Deduced)) { Ty = DeduceTemplateSpecializationFromInitializer(TInfo, Entity, @@ -1474,6 +1477,33 @@ if (Ty.isNull()) return ExprError(); Entity = InitializedEntity::InitializeTemporary(TInfo, Ty); + } else if (Deduced) { + auto Inits = Exprs; + if (Exprs.size() == 1) { + if (auto p = dyn_cast_or_null(Exprs[0])) { + Inits = MultiExprArg(p->getInits(), p->getNumInits()); + } + } + + if (Inits.empty()) + return ExprError(Diag(TyBeginLoc, diag::err_auto_expr_init_no_expression) + << Ty << FullRange); + if (Inits.size() > 1) { + Expr *FirstBad = Inits[1]; + return ExprError(Diag(FirstBad->getBeginLoc(), + diag::err_auto_expr_init_multiple_expressions) + << Ty << FullRange); + } + Expr *Deduce = Inits[0]; + QualType DeducedType; + if (DeduceAutoType(TInfo, Deduce, DeducedType) == DAR_Failed) + return ExprError(Diag(TyBeginLoc, diag::err_auto_expr_deduction_failure) + << Ty << Deduce->getType() + << FullRange << Deduce->getSourceRange()); + if (DeducedType.isNull()) + return ExprError(); + Ty = DeducedType; + Entity = InitializedEntity::InitializeTemporary(TInfo, Ty); } if (Ty->isDependentType() || CallExpr::hasAnyTypeDependentArguments(Exprs)) { diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -3395,6 +3395,8 @@ // class template argument deduction)? bool IsCXXAutoType = (Auto && Auto->getKeyword() != AutoTypeKeyword::GNUAutoType); + bool IsAutoKeyword = + (Auto && Auto->getKeyword() == AutoTypeKeyword::Auto); bool IsDeducedReturnType = false; switch (D.getContext()) { @@ -3414,8 +3416,7 @@ InventedTemplateParameterInfo *Info = nullptr; if (D.getContext() == DeclaratorContext::Prototype) { // With concepts we allow 'auto' in function parameters. - if (!SemaRef.getLangOpts().CPlusPlus20 || !Auto || - Auto->getKeyword() != AutoTypeKeyword::Auto) { + if (!SemaRef.getLangOpts().CPlusPlus20 || !IsAutoKeyword) { Error = 0; break; } else if (!SemaRef.getCurScope()->isFunctionDeclarationScope()) { @@ -3426,8 +3427,7 @@ Info = &SemaRef.InventedParameterInfos.back(); } else { // In C++14, generic lambdas allow 'auto' in their parameters. - if (!SemaRef.getLangOpts().CPlusPlus14 || !Auto || - Auto->getKeyword() != AutoTypeKeyword::Auto) { + if (!SemaRef.getLangOpts().CPlusPlus14 || !IsAutoKeyword) { Error = 16; break; } @@ -3505,6 +3505,8 @@ case DeclaratorContext::FunctionalCast: if (isa(Deduced)) break; + if (SemaRef.getLangOpts().CPlusPlus2b && IsAutoKeyword) + break; // auto(x) LLVM_FALLTHROUGH; case DeclaratorContext::TypeName: Error = 15; // Generic diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp @@ -51,8 +51,8 @@ (void)reinterpret_cast(&n); // expected-error{{'auto' not allowed here}} (void)const_cast(n); // expected-error{{'auto' not allowed here}} (void)*(auto*)(&n); // expected-error{{'auto' not allowed here}} - (void)auto(n); // expected-error{{expected expression}} - (void)auto{n}; // expected-error{{expected expression}} + (void)auto(n); // expected-error{{'auto' not allowed here}} + (void)auto{n}; // expected-error{{'auto' not allowed here}} } template class C { }; // expected-error{{'auto' not allowed in template parameter}} diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.auto.deduct/p2.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.auto.deduct/p2.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.auto.deduct/p2.cpp @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -std=c++2b -verify %s +// expected-no-diagnostics + +void test_decay() { + int v[3]; + static_assert(__is_same(decltype(auto(v)), int *)); + static_assert(__is_same(decltype(auto("lit")), char const *)); + + int fn(char *); + static_assert(__is_same(decltype(auto(fn)), int (*)(char *))); + + constexpr long i = 1; + static_assert(__is_same(decltype(auto(1L)), long)); + static_assert(__is_same(decltype(i), long const)); + static_assert(__is_same(decltype(auto(i)), long)); + + class A { + } a; + + A &lr = a; + A const &lrc = a; + A &&rr = static_cast(a); + A const &&rrc = static_cast(a); + + static_assert(__is_same(decltype(auto(lr)), A)); + static_assert(__is_same(decltype(auto(lrc)), A)); + static_assert(__is_same(decltype(auto(rr)), A)); + static_assert(__is_same(decltype(auto(rrc)), A)); +} + +class cmdline_parser { +public: + cmdline_parser(char const *); + auto add_option(char const *, char const *) && -> cmdline_parser &&; +}; + +void test_rvalue_fluent_interface() { + auto cmdline = cmdline_parser("driver"); + auto internal = auto {cmdline}.add_option("--dump-full", "do not minimize dump"); +} + +class A { + int x; + friend void f(A &&); + +public: + A(); + + auto test_access() { + f(A(*this)); // ok + f(auto(*this)); // ok in P0849 + } + +protected: + A(const A &); +}; diff --git a/clang/test/CXX/expr/expr.post/expr.type.conv/p1-2b.cpp b/clang/test/CXX/expr/expr.post/expr.type.conv/p1-2b.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/expr/expr.post/expr.type.conv/p1-2b.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -std=c++2b -verify %s + +template +void foo(T); + +struct A { + int m; + char g(int); + float g(double); +} a{1}; + +void diagnostics() { + foo(auto()); // expected-error {{initializer for functional-style cast to 'auto' is empty}} + foo(auto {}); // expected-error {{initializer for functional-style cast to 'auto' is empty}} + foo(auto({})); // expected-error {{initializer for functional-style cast to 'auto' is empty}} + + foo(auto(a)); + foo(auto {a}); + foo(auto(a)); + + foo(auto(&A::g)); // expected-error {{functional-style cast to 'auto' has incompatible initializer of type ''}} + + foo(auto(a, 3.14)); // expected-error {{initializer for functional-style cast to 'auto' contains multiple expressions}} + foo(auto {a, 3.14}); // expected-error {{initializer for functional-style cast to 'auto' contains multiple expressions}} + foo(auto({a, 3.14})); // expected-error {{initializer for functional-style cast to 'auto' contains multiple expressions}} +}