From 8210ed558662bd80f16bc96e482974120d2ca2bd Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 13 Jan 2017 02:22:01 +0000 Subject: [PATCH] Implement DR1265 (wg21.link/cwg1265). Diasllow a declaration using the 'auto' type specifier from using two different meanings of it at once, or from declaring multiple functions with deduced return types or introducing multiple trailing return types. The standard does not technically disallow the multiple trailing return types case if all the declarators declare variables (such as function pointers with trailing return types), but we disallow that too, following the clear intent. llvm-svn: 291880 --- clang/include/clang/AST/Type.h | 5 + .../clang/Basic/DiagnosticSemaKinds.td | 4 + clang/lib/AST/Type.cpp | 45 +++++--- clang/lib/Sema/SemaDecl.cpp | 107 ++++++++++-------- .../dcl.spec/dcl.type/dcl.spec.auto/p7.cpp | 35 ++++-- clang/test/CXX/drs/dr12xx.cpp | 20 +++- clang/test/CXX/drs/dr13xx.cpp | 19 ++++ clang/www/cxx_dr_status.html | 4 +- 8 files changed, 163 insertions(+), 76 deletions(-) diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 744a408e5796..c8b488e916ca 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -1867,6 +1867,11 @@ public: /// types, but not through decltype or typedefs. AutoType *getContainedAutoType() const; + /// Determine whether this type was written with a leading 'auto' + /// corresponding to a trailing return type (possibly for a nested + /// function type within a pointer to function type or similar). + bool hasAutoForTrailingReturnType() const; + /// Member-template getAs'. Look through sugar for /// an instance of \. This scheme will eventually /// replace the specific getAsXXXX methods above. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3971cf60d5e0..23d61c0848b3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1900,6 +1900,10 @@ def err_auto_new_deduction_failure : Error< def err_auto_different_deductions : Error< "'%select{auto|decltype(auto)|__auto_type}0' deduced as %1 in declaration " "of %2 and deduced as %3 in declaration of %4">; +def err_auto_non_deduced_not_alone : Error< + "%select{function with deduced return type|" + "declaration with trailing return type}0 " + "must be the only declaration in its group">; def err_implied_std_initializer_list_not_found : Error< "cannot deduce type of initializer list because std::initializer_list was " "not found; include ">; diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 0d0cd2e305be..8a93f7c5d1e3 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -1560,60 +1560,73 @@ TagDecl *Type::getAsTagDecl() const { namespace { class GetContainedAutoVisitor : - public TypeVisitor { + public TypeVisitor { + bool Syntactic; public: - using TypeVisitor::Visit; - AutoType *Visit(QualType T) { + GetContainedAutoVisitor(bool Syntactic = false) : Syntactic(Syntactic) {} + + using TypeVisitor::Visit; + Type *Visit(QualType T) { if (T.isNull()) return nullptr; return Visit(T.getTypePtr()); } // The 'auto' type itself. - AutoType *VisitAutoType(const AutoType *AT) { + Type *VisitAutoType(const AutoType *AT) { return const_cast(AT); } // Only these types can contain the desired 'auto' type. - AutoType *VisitPointerType(const PointerType *T) { + Type *VisitPointerType(const PointerType *T) { return Visit(T->getPointeeType()); } - AutoType *VisitBlockPointerType(const BlockPointerType *T) { + Type *VisitBlockPointerType(const BlockPointerType *T) { return Visit(T->getPointeeType()); } - AutoType *VisitReferenceType(const ReferenceType *T) { + Type *VisitReferenceType(const ReferenceType *T) { return Visit(T->getPointeeTypeAsWritten()); } - AutoType *VisitMemberPointerType(const MemberPointerType *T) { + Type *VisitMemberPointerType(const MemberPointerType *T) { return Visit(T->getPointeeType()); } - AutoType *VisitArrayType(const ArrayType *T) { + Type *VisitArrayType(const ArrayType *T) { return Visit(T->getElementType()); } - AutoType *VisitDependentSizedExtVectorType( + Type *VisitDependentSizedExtVectorType( const DependentSizedExtVectorType *T) { return Visit(T->getElementType()); } - AutoType *VisitVectorType(const VectorType *T) { + Type *VisitVectorType(const VectorType *T) { return Visit(T->getElementType()); } - AutoType *VisitFunctionType(const FunctionType *T) { + Type *VisitFunctionProtoType(const FunctionProtoType *T) { + if (Syntactic && T->hasTrailingReturn()) + return const_cast(T); + return VisitFunctionType(T); + } + Type *VisitFunctionType(const FunctionType *T) { return Visit(T->getReturnType()); } - AutoType *VisitParenType(const ParenType *T) { + Type *VisitParenType(const ParenType *T) { return Visit(T->getInnerType()); } - AutoType *VisitAttributedType(const AttributedType *T) { + Type *VisitAttributedType(const AttributedType *T) { return Visit(T->getModifiedType()); } - AutoType *VisitAdjustedType(const AdjustedType *T) { + Type *VisitAdjustedType(const AdjustedType *T) { return Visit(T->getOriginalType()); } }; } AutoType *Type::getContainedAutoType() const { - return GetContainedAutoVisitor().Visit(this); + return cast_or_null(GetContainedAutoVisitor().Visit(this)); +} + +bool Type::hasAutoForTrailingReturnType() const { + return dyn_cast_or_null( + GetContainedAutoVisitor(true).Visit(this)); } bool Type::hasIntegerRepresentation() const { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index adcf2ee00e75..fe1775e1eb5b 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -11045,6 +11045,11 @@ Sema::FinalizeDeclaration(Decl *ThisDecl) { } } +static bool hasDeducedAuto(DeclaratorDecl *DD) { + auto *VD = dyn_cast(DD); + return VD && !VD->getType()->hasAutoForTrailingReturnType(); +} + Sema::DeclGroupPtrTy Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS, ArrayRef Group) { SmallVector Decls; @@ -11055,29 +11060,46 @@ Sema::DeclGroupPtrTy Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS, DeclaratorDecl *FirstDeclaratorInGroup = nullptr; DecompositionDecl *FirstDecompDeclaratorInGroup = nullptr; bool DiagnosedMultipleDecomps = false; + DeclaratorDecl *FirstNonDeducedAutoInGroup = nullptr; + bool DiagnosedNonDeducedAuto = false; for (unsigned i = 0, e = Group.size(); i != e; ++i) { if (Decl *D = Group[i]) { - auto *DD = dyn_cast(D); - if (DD && !FirstDeclaratorInGroup) - FirstDeclaratorInGroup = DD; + // For declarators, there are some additional syntactic-ish checks we need + // to perform. + if (auto *DD = dyn_cast(D)) { + if (!FirstDeclaratorInGroup) + FirstDeclaratorInGroup = DD; + if (!FirstDecompDeclaratorInGroup) + FirstDecompDeclaratorInGroup = dyn_cast(D); + if (!FirstNonDeducedAutoInGroup && DS.containsPlaceholderType() && + !hasDeducedAuto(DD)) + FirstNonDeducedAutoInGroup = DD; - auto *Decomp = dyn_cast(D); - if (Decomp && !FirstDecompDeclaratorInGroup) - FirstDecompDeclaratorInGroup = Decomp; + if (FirstDeclaratorInGroup != DD) { + // A decomposition declaration cannot be combined with any other + // declaration in the same group. + if (FirstDecompDeclaratorInGroup && !DiagnosedMultipleDecomps) { + Diag(FirstDecompDeclaratorInGroup->getLocation(), + diag::err_decomp_decl_not_alone) + << FirstDeclaratorInGroup->getSourceRange() + << DD->getSourceRange(); + DiagnosedMultipleDecomps = true; + } - // A decomposition declaration cannot be combined with any other - // declaration in the same group. - auto *OtherDD = FirstDeclaratorInGroup; - if (OtherDD == FirstDecompDeclaratorInGroup) - OtherDD = DD; - if (OtherDD && FirstDecompDeclaratorInGroup && - OtherDD != FirstDecompDeclaratorInGroup && - !DiagnosedMultipleDecomps) { - Diag(FirstDecompDeclaratorInGroup->getLocation(), - diag::err_decomp_decl_not_alone) - << OtherDD->getSourceRange(); - DiagnosedMultipleDecomps = true; + // A declarator that uses 'auto' in any way other than to declare a + // variable with a deduced type cannot be combined with any other + // declarator in the same group. + if (FirstNonDeducedAutoInGroup && !DiagnosedNonDeducedAuto) { + Diag(FirstNonDeducedAutoInGroup->getLocation(), + diag::err_auto_non_deduced_not_alone) + << FirstNonDeducedAutoInGroup->getType() + ->hasAutoForTrailingReturnType() + << FirstDeclaratorInGroup->getSourceRange() + << DD->getSourceRange(); + DiagnosedNonDeducedAuto = true; + } + } } Decls.push_back(D); @@ -11105,38 +11127,27 @@ Sema::BuildDeclaratorGroup(MutableArrayRef Group) { // deduction, the program is ill-formed. if (Group.size() > 1) { QualType Deduced; - CanQualType DeducedCanon; VarDecl *DeducedDecl = nullptr; for (unsigned i = 0, e = Group.size(); i != e; ++i) { - if (VarDecl *D = dyn_cast(Group[i])) { - AutoType *AT = D->getType()->getContainedAutoType(); - // FIXME: DR1265: if we have a function pointer declaration, we can have - // an 'auto' from a trailing return type. In that case, the return type - // must match the various other uses of 'auto'. - if (!AT) - continue; - // Don't reissue diagnostics when instantiating a template. - if (D->isInvalidDecl()) - break; - QualType U = AT->getDeducedType(); - if (!U.isNull()) { - CanQualType UCanon = Context.getCanonicalType(U); - if (Deduced.isNull()) { - Deduced = U; - DeducedCanon = UCanon; - DeducedDecl = D; - } else if (DeducedCanon != UCanon) { - Diag(D->getTypeSourceInfo()->getTypeLoc().getBeginLoc(), - diag::err_auto_different_deductions) - << (unsigned)AT->getKeyword() - << Deduced << DeducedDecl->getDeclName() - << U << D->getDeclName() - << DeducedDecl->getInit()->getSourceRange() - << D->getInit()->getSourceRange(); - D->setInvalidDecl(); - break; - } - } + VarDecl *D = dyn_cast(Group[i]); + if (!D || D->isInvalidDecl()) + break; + AutoType *AT = D->getType()->getContainedAutoType(); + if (!AT || AT->getDeducedType().isNull()) + continue; + if (Deduced.isNull()) { + Deduced = AT->getDeducedType(); + DeducedDecl = D; + } else if (!Context.hasSameType(AT->getDeducedType(), Deduced)) { + Diag(D->getTypeSourceInfo()->getTypeLoc().getBeginLoc(), + diag::err_auto_different_deductions) + << (unsigned)AT->getKeyword() + << Deduced << DeducedDecl->getDeclName() + << AT->getDeducedType() << D->getDeclName() + << DeducedDecl->getInit()->getSourceRange() + << D->getInit()->getSourceRange(); + D->setInvalidDecl(); + break; } } } diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7.cpp index 8d789bdd5ad3..e9294d7f4362 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7.cpp @@ -1,3 +1,4 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++14 // RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 // RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++98 -Wno-c++11-extensions void f() { @@ -19,22 +20,42 @@ void f() { } void g() { +#if __has_feature(cxx_trailing_return) auto a = 0, -#if __has_feature(cxx_trailing_return) - (*b)() -> void, -#endif + (*b)() -> void, // expected-error {{declaration with trailing return type must be the only declaration in its group}} c = 0; - auto d = 0, // expected-error {{'auto' deduced as 'int' in declaration of 'd' and deduced as 'double' in declaration of 'f'}} -#if __has_feature(cxx_trailing_return) - (*e)() -> void, -#endif + auto d = 0, + e() -> void, // expected-error {{declaration with trailing return type must be the only declaration in its group}} f = 0.0; + auto x() -> void, // expected-error {{declaration with trailing return type must be the only declaration in its group}} + y() -> void; +#endif #if __has_feature(cxx_decltype) auto g = 0ull, h = decltype(g)(0); #endif } +#if __has_feature(cxx_trailing_return) +int F(); +auto p = 0, (*q)() -> auto = F; // expected-error {{declaration with trailing return type must be the only declaration in its group}} + #if __cplusplus < 201402L + // expected-error@-2 {{'auto' not allowed in function return type}} + #endif +#endif + +#if __cplusplus >= 201402L +namespace DeducedReturnType { + auto a = 0, + b(), // expected-error {{function with deduced return type must be the only declaration in its group}} + c = 0.0; + auto d(), // expected-error {{function with deduced return type must be the only declaration in its group}} + e = 1; + auto f(), // expected-error {{function with deduced return type must be the only declaration in its group}} + g(); +} +#endif + template void h() { auto a = T(), *b = &a; #if __has_feature(cxx_decltype) diff --git a/clang/test/CXX/drs/dr12xx.cpp b/clang/test/CXX/drs/dr12xx.cpp index 45b33f9d7daf..24039a1cd240 100644 --- a/clang/test/CXX/drs/dr12xx.cpp +++ b/clang/test/CXX/drs/dr12xx.cpp @@ -14,7 +14,7 @@ namespace dr1213 { // dr1213: 4 #endif } -namespace dr1250 { // dr1250: 3.9 +namespace dr1250 { // dr1250: 3.9 struct Incomplete; struct Base { @@ -24,9 +24,23 @@ struct Base { struct Derived : Base { virtual Incomplete *meow(); }; -} // dr1250 +} -namespace dr1295 { // dr1295: 4 +namespace dr1265 { // dr1265: 5 +#if __cplusplus >= 201103L + auto a = 0, b() -> int; // expected-error {{declaration with trailing return type must be the only declaration in its group}} + auto b() -> int, d = 0; // expected-error {{declaration with trailing return type must be the only declaration in its group}} + auto e() -> int, f() -> int; // expected-error {{declaration with trailing return type must be the only declaration in its group}} +#endif + +#if __cplusplus >= 201402L + auto g(), h = 0; // expected-error {{function with deduced return type must be the only declaration in its group}} + auto i = 0, j(); // expected-error {{function with deduced return type must be the only declaration in its group}} + auto k(), l(); // expected-error {{function with deduced return type must be the only declaration in its group}} +#endif +} + +namespace dr1295 { // dr1295: 4 struct X { unsigned bitfield : 4; }; diff --git a/clang/test/CXX/drs/dr13xx.cpp b/clang/test/CXX/drs/dr13xx.cpp index f35ead7b5e94..a4223ffb9080 100644 --- a/clang/test/CXX/drs/dr13xx.cpp +++ b/clang/test/CXX/drs/dr13xx.cpp @@ -3,6 +3,16 @@ // RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++1z %s -verify -fexceptions -fcxx-exceptions -pedantic-errors +__extension__ typedef __SIZE_TYPE__ size_t; + +namespace std { + template struct initializer_list { + const T *ptr; + size_t n; + initializer_list(const T*, size_t); + }; +} + namespace dr1315 { // dr1315: partial template struct A {}; template // expected-note {{non-deducible template parameter 'I'}} @@ -159,6 +169,15 @@ namespace dr1346 { // dr1346: 3.5 #endif } +namespace dr1347 { // dr1347: yes + auto x = 5, *y = &x; // expected-error 0-1{{extension}} + auto z = y, *q = y; // expected-error {{'auto' deduced as 'int *' in declaration of 'z' and deduced as 'int' in declaration of 'q'}} expected-error 0-1{{extension}} +#if __cplusplus >= 201103L + auto a = 5, b = {1, 2}; // expected-error {{'auto' deduced as 'int' in declaration of 'a' and deduced as 'std::initializer_list' in declaration of 'b'}} + auto (*fp)(int) -> int, i = 0; // expected-error {{declaration with trailing return type must be the only declaration in its group}} +#endif +} + namespace dr1359 { // dr1359: 3.5 #if __cplusplus >= 201103L union A { constexpr A() = default; }; diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index a0781458a852..ffc076512446 100644 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -7405,7 +7405,7 @@ and POD class 1265 CD3 Mixed use of the auto specifier - Unknown + SVN 1266 @@ -7897,7 +7897,7 @@ and POD class 1347 CD3 Consistency of auto in multiple-declarator declarations - Unknown + Yes 1348