From ea21696b8711f1e5d22d85f081f3fbc60e284b2b Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 11 Nov 2015 02:02:15 +0000 Subject: [PATCH] Add support for GCC's '__auto_type' extension, per the GCC manual: https://gcc.gnu.org/onlinedocs/gcc/Typeof.html Differences from the GCC extension: * __auto_type is also permitted in C++ (but only in places where it could appear in C), allowing its use in headers that might be shared across C and C++, or used from C++98 * __auto_type can be combined with a declarator, as with C++ auto (for instance, "__auto_type *p") * multiple variables can be declared in a single __auto_type declaration, with the C++ semantics (the deduced type must be the same in each case) This patch also adds a missing restriction on applying typeof to a bit-field, which GCC has historically rejected in C (due to lack of clarity as to whether the operand should be promoted). The same restriction also applies to __auto_type in C (in both GCC and Clang). This also fixes PR25449. Patch by Nicholas Allegra! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@252690 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 2 +- include/clang/AST/Type.h | 34 ++++-- include/clang/Basic/DiagnosticGroups.td | 2 + include/clang/Basic/DiagnosticParseKinds.td | 3 + include/clang/Basic/DiagnosticSemaKinds.td | 26 +++-- include/clang/Basic/Specifiers.h | 1 + include/clang/Basic/TokenKinds.def | 1 + include/clang/Sema/DeclSpec.h | 4 +- lib/AST/ASTContext.cpp | 10 +- lib/AST/ASTImporter.cpp | 2 +- lib/AST/ItaniumMangle.cpp | 6 +- lib/AST/MicrosoftMangle.cpp | 2 + lib/AST/Type.cpp | 2 +- lib/AST/TypePrinter.cpp | 6 +- lib/Format/TokenAnnotator.cpp | 2 +- lib/Parse/ParseDecl.cpp | 6 + lib/Parse/ParseObjc.cpp | 1 + lib/Parse/ParseTentative.cpp | 3 + lib/Sema/DeclSpec.cpp | 2 + lib/Sema/SemaDecl.cpp | 8 +- lib/Sema/SemaExpr.cpp | 8 +- lib/Sema/SemaTemplateDeduction.cpp | 12 +- lib/Sema/SemaTemplateVariadic.cpp | 1 + lib/Sema/SemaType.cpp | 109 +++++++++++------- lib/Sema/TreeTransform.h | 8 +- lib/Serialization/ASTReader.cpp | 4 +- lib/Serialization/ASTWriter.cpp | 2 +- .../dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp | 6 +- .../dcl.spec/dcl.type/dcl.spec.auto/p7-1y.cpp | 2 +- test/Sema/bitfield.c | 3 +- test/Sema/exprs.c | 1 + test/SemaCXX/auto-cxx0x.cpp | 3 + test/SemaCXX/cxx98-compat.cpp | 3 + 33 files changed, 192 insertions(+), 93 deletions(-) diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 72bbb606df..a6d97aaec2 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -1299,7 +1299,7 @@ public: UnaryTransformType::UTTKind UKind) const; /// \brief C++11 deduced auto type. - QualType getAutoType(QualType DeducedType, bool IsDecltypeAuto, + QualType getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, bool IsDependent) const; /// \brief C++11 deduction pattern for 'auto' type. diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index d080535ba7..e9aadec802 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -1210,6 +1210,16 @@ enum RefQualifierKind { RQ_RValue }; +/// Which keyword(s) were used to create an AutoType. +enum class AutoTypeKeyword { + /// \brief auto + Auto, + /// \brief decltype(auto) + DecltypeAuto, + /// \brief __auto_type (GNU extension) + GNUAutoType +}; + /// The base class of the type hierarchy. /// /// A central concept with types is that each type always has a canonical @@ -1428,8 +1438,9 @@ protected: unsigned : NumTypeBits; - /// Was this placeholder type spelled as 'decltype(auto)'? - unsigned IsDecltypeAuto : 1; + /// Was this placeholder type spelled as 'auto', 'decltype(auto)', + /// or '__auto_type'? AutoTypeKeyword value. + unsigned Keyword : 2; }; union { @@ -3902,8 +3913,7 @@ public: /// is no deduced type and an auto type is canonical. In the latter case, it is /// also a dependent type. class AutoType : public Type, public llvm::FoldingSetNode { - AutoType(QualType DeducedType, bool IsDecltypeAuto, - bool IsDependent) + AutoType(QualType DeducedType, AutoTypeKeyword Keyword, bool IsDependent) : Type(Auto, DeducedType.isNull() ? QualType(this, 0) : DeducedType, /*Dependent=*/IsDependent, /*InstantiationDependent=*/IsDependent, /*VariablyModified=*/false, @@ -3911,13 +3921,18 @@ class AutoType : public Type, public llvm::FoldingSetNode { ? false : DeducedType->containsUnexpandedParameterPack()) { assert((DeducedType.isNull() || !IsDependent) && "auto deduced to dependent type"); - AutoTypeBits.IsDecltypeAuto = IsDecltypeAuto; + AutoTypeBits.Keyword = (unsigned)Keyword; } friend class ASTContext; // ASTContext creates these public: - bool isDecltypeAuto() const { return AutoTypeBits.IsDecltypeAuto; } + bool isDecltypeAuto() const { + return getKeyword() == AutoTypeKeyword::DecltypeAuto; + } + AutoTypeKeyword getKeyword() const { + return (AutoTypeKeyword)AutoTypeBits.Keyword; + } bool isSugared() const { return !isCanonicalUnqualified(); } QualType desugar() const { return getCanonicalTypeInternal(); } @@ -3932,14 +3947,13 @@ public: } void Profile(llvm::FoldingSetNodeID &ID) { - Profile(ID, getDeducedType(), isDecltypeAuto(), - isDependentType()); + Profile(ID, getDeducedType(), getKeyword(), isDependentType()); } static void Profile(llvm::FoldingSetNodeID &ID, QualType Deduced, - bool IsDecltypeAuto, bool IsDependent) { + AutoTypeKeyword Keyword, bool IsDependent) { ID.AddPointer(Deduced.getAsOpaquePtr()); - ID.AddBoolean(IsDecltypeAuto); + ID.AddInteger((unsigned)Keyword); ID.AddBoolean(IsDependent); } diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 4b94b186cd..0b030168aa 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -24,6 +24,7 @@ def : DiagGroup<"aggregate-return">; def GNUAlignofExpression : DiagGroup<"gnu-alignof-expression">; def AmbigMemberTemplate : DiagGroup<"ambiguous-member-template">; def GNUAnonymousStruct : DiagGroup<"gnu-anonymous-struct">; +def GNUAutoType : DiagGroup<"gnu-auto-type">; def ArrayBounds : DiagGroup<"array-bounds">; def ArrayBoundsPointerArithmetic : DiagGroup<"array-bounds-pointer-arithmetic">; def Availability : DiagGroup<"availability">; @@ -707,6 +708,7 @@ def C99 : DiagGroup<"c99-extensions">; // A warning group for warnings about GCC extensions. def GNU : DiagGroup<"gnu", [GNUAlignofExpression, GNUAnonymousStruct, + GNUAutoType, GNUBinaryLiteral, GNUCaseRange, GNUComplexInteger, GNUCompoundLiteralInitializer, GNUConditionalOmittedOperand, GNUDesignator, diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index bf0a128a70..6e7fbde587 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -299,6 +299,9 @@ def ext_decltype_auto_type_specifier : ExtWarn< def warn_cxx11_compat_decltype_auto_type_specifier : Warning< "'decltype(auto)' type specifier is incompatible with C++ standards before " "C++14">, InGroup, DefaultIgnore; +def ext_auto_type : Extension< + "'__auto_type' is a GNU extension">, + InGroup; def ext_for_range : ExtWarn< "range-based for loop is a C++11 extension">, InGroup; def warn_cxx98_compat_for_range : Warning< diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 9b21c61dce..4306254305 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1664,18 +1664,22 @@ def warn_cxx98_compat_auto_type_specifier : Warning< "'auto' type specifier is incompatible with C++98">, InGroup, DefaultIgnore; def err_auto_variable_cannot_appear_in_own_initializer : Error< - "variable %0 declared with 'auto' type cannot appear in its own initializer">; + "variable %0 declared with %select{'auto'|'decltype(auto)'|'__auto_type'}1 " + "type cannot appear in its own initializer">; def err_illegal_decl_array_of_auto : Error< "'%0' declared as array of %1">; def err_new_array_of_auto : Error< "cannot allocate array of 'auto'">; def err_auto_not_allowed : Error< - "%select{'auto'|'decltype(auto)'}0 not allowed %select{in function prototype" - "|in non-static struct member" - "|in non-static union member|in non-static class member|in interface member" + "%select{'auto'|'decltype(auto)'|'__auto_type'}0 not allowed " + "%select{in function prototype" + "|in non-static struct member|in struct member" + "|in non-static union member|in union member" + "|in non-static class member|in interface member" "|in exception declaration|in template parameter|in block literal" "|in template argument|in typedef|in type alias|in function return type" - "|in conversion function type|here|in lambda parameter}1">; + "|in conversion function type|here|in lambda parameter" + "|in type allocated by 'new'|in K&R-style function parameter}1">; def err_auto_not_allowed_var_inst : Error< "'auto' variable template instantiation is not allowed">; def err_auto_var_requires_init : Error< @@ -1709,8 +1713,8 @@ def err_auto_var_deduction_failure_from_init_list : Error< def err_auto_new_deduction_failure : Error< "new expression for type %0 has incompatible constructor argument of type %1">; def err_auto_different_deductions : Error< - "'%select{auto|decltype(auto)}0' deduced as %1 in declaration of %2 and " - "deduced as %3 in declaration of %4">; + "'%select{auto|decltype(auto)|__auto_type}0' deduced as %1 in declaration " + "of %2 and deduced as %3 in declaration of %4">; def err_implied_std_initializer_list_not_found : Error< "cannot deduce type of initializer list because std::initializer_list was " "not found; include ">; @@ -1720,6 +1724,10 @@ def warn_dangling_std_initializer_list : Warning< "array backing the initializer list will be destroyed at the end of " "%select{the full-expression|the constructor}0">, InGroup>; +def err_auto_init_list_from_c : Error< + "cannot use __auto_type with initializer list in C">; +def err_auto_bitfield : Error< + "cannot pass bit-field as __auto_type initializer in C">; // C++1y decltype(auto) type def err_decltype_auto_cannot_be_combined : Error< @@ -4812,8 +4820,8 @@ def err_sizeof_alignof_function_type : Error< "function type">; def err_openmp_default_simd_align_expr : Error< "invalid application of '__builtin_omp_required_simd_align' to an expression, only type is allowed">; -def err_sizeof_alignof_bitfield : Error< - "invalid application of '%select{sizeof|alignof}0' to bit-field">; +def err_sizeof_alignof_typeof_bitfield : Error< + "invalid application of '%select{sizeof|alignof|typeof}0' to bit-field">; def err_alignof_member_of_incomplete_type : Error< "invalid application of 'alignof' to a field of a class still being defined">; def err_vecstep_non_scalar_vector_type : Error< diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h index 47d2f31c0f..e5f1f834f7 100644 --- a/include/clang/Basic/Specifiers.h +++ b/include/clang/Basic/Specifiers.h @@ -64,6 +64,7 @@ namespace clang { TST_underlyingType, // __underlying_type for C++11 TST_auto, // C++11 auto TST_decltype_auto, // C++1y decltype(auto) + TST_auto_type, // __auto_type extension TST_unknown_anytype, // __unknown_anytype extension TST_atomic, // C11 _Atomic TST_error // erroneous type diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index 1e0ec117d0..9252d9947a 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -384,6 +384,7 @@ KEYWORD(__real , KEYALL) KEYWORD(__thread , KEYALL) KEYWORD(__FUNCTION__ , KEYALL) KEYWORD(__PRETTY_FUNCTION__ , KEYALL) +KEYWORD(__auto_type , KEYALL) // GNU Extensions (outside impl-reserved namespace) KEYWORD(typeof , KEYGNU) diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index f809c58e05..7bce0cdcfc 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -301,6 +301,7 @@ public: static const TST TST_decltype_auto = clang::TST_decltype_auto; static const TST TST_underlyingType = clang::TST_underlyingType; static const TST TST_auto = clang::TST_auto; + static const TST TST_auto_type = clang::TST_auto_type; static const TST TST_unknown_anytype = clang::TST_unknown_anytype; static const TST TST_atomic = clang::TST_atomic; static const TST TST_error = clang::TST_error; @@ -512,7 +513,8 @@ public: void setTypeofParensRange(SourceRange range) { TypeofParensRange = range; } bool containsPlaceholderType() const { - return TypeSpecType == TST_auto || TypeSpecType == TST_decltype_auto; + return (TypeSpecType == TST_auto || TypeSpecType == TST_auto_type || + TypeSpecType == TST_decltype_auto); } bool hasTagDefinition() const; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 4009915619..18240e79d8 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3983,20 +3983,20 @@ QualType ASTContext::getUnaryTransformType(QualType BaseType, /// getAutoType - Return the uniqued reference to the 'auto' type which has been /// deduced to the given type, or to the canonical undeduced 'auto' type, or the /// canonical deduced-but-dependent 'auto' type. -QualType ASTContext::getAutoType(QualType DeducedType, bool IsDecltypeAuto, +QualType ASTContext::getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, bool IsDependent) const { - if (DeducedType.isNull() && !IsDecltypeAuto && !IsDependent) + if (DeducedType.isNull() && Keyword == AutoTypeKeyword::Auto && !IsDependent) return getAutoDeductType(); // Look in the folding set for an existing type. void *InsertPos = nullptr; llvm::FoldingSetNodeID ID; - AutoType::Profile(ID, DeducedType, IsDecltypeAuto, IsDependent); + AutoType::Profile(ID, DeducedType, Keyword, IsDependent); if (AutoType *AT = AutoTypes.FindNodeOrInsertPos(ID, InsertPos)) return QualType(AT, 0); AutoType *AT = new (*this, TypeAlignment) AutoType(DeducedType, - IsDecltypeAuto, + Keyword, IsDependent); Types.push_back(AT); if (InsertPos) @@ -4036,7 +4036,7 @@ QualType ASTContext::getAtomicType(QualType T) const { QualType ASTContext::getAutoDeductType() const { if (AutoDeductTy.isNull()) AutoDeductTy = QualType( - new (*this, TypeAlignment) AutoType(QualType(), /*decltype(auto)*/false, + new (*this, TypeAlignment) AutoType(QualType(), AutoTypeKeyword::Auto, /*dependent*/false), 0); return AutoDeductTy; diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index ccc6931a5f..824cc18010 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -1746,7 +1746,7 @@ QualType ASTNodeImporter::VisitAutoType(const AutoType *T) { return QualType(); } - return Importer.getToContext().getAutoType(ToDeduced, T->isDecltypeAuto(), + return Importer.getToContext().getAutoType(ToDeduced, T->getKeyword(), /*IsDependent*/false); } diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index c3432839c2..7f2ef495a2 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -2653,9 +2653,11 @@ void CXXNameMangler::mangleType(const UnaryTransformType *T) { void CXXNameMangler::mangleType(const AutoType *T) { QualType D = T->getDeducedType(); // ::= Da # dependent auto - if (D.isNull()) + if (D.isNull()) { + assert(T->getKeyword() != AutoTypeKeyword::GNUAutoType && + "shouldn't need to mangle __auto_type!"); Out << (T->isDecltypeAuto() ? "Dc" : "Da"); - else + } else mangleType(D); } diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index 5c30ae28b3..01e17588fe 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -1818,6 +1818,8 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T, Out << '?'; mangleQualifiers(ResultType.getLocalQualifiers(), /*IsMember=*/false); Out << '?'; + assert(AT->getKeyword() != AutoTypeKeyword::GNUAutoType && + "shouldn't need to mangle __auto_type!"); mangleSourceName(AT->isDecltypeAuto() ? "" : ""); Out << '@'; } else { diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 40fe903fea..611218ff91 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -948,7 +948,7 @@ public: == T->getDeducedType().getAsOpaquePtr()) return QualType(T, 0); - return Ctx.getAutoType(deducedType, T->isDecltypeAuto(), + return Ctx.getAutoType(deducedType, T->getKeyword(), T->isDependentType()); } diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 0bb50c6ba8..b4810d63c8 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -835,7 +835,11 @@ void TypePrinter::printAutoBefore(const AutoType *T, raw_ostream &OS) { if (!T->getDeducedType().isNull()) { printBefore(T->getDeducedType(), OS); } else { - OS << (T->isDecltypeAuto() ? "decltype(auto)" : "auto"); + switch (T->getKeyword()) { + case AutoTypeKeyword::Auto: OS << "auto"; break; + case AutoTypeKeyword::DecltypeAuto: OS << "decltype(auto)"; break; + case AutoTypeKeyword::GNUAutoType: OS << "__auto_type"; break; + } spaceBeforePlaceHolder(OS); } } diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index b44a34115f..8f08828b5f 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -905,7 +905,7 @@ private: (!Line.MightBeFunctionDecl || Current.NestingLevel != 0)) { Contexts.back().FirstStartOfName = &Current; Current.Type = TT_StartOfName; - } else if (Current.is(tok::kw_auto)) { + } else if (Current.isOneOf(tok::kw_auto, tok::kw___auto_type)) { AutoFound = true; } else if (Current.is(tok::arrow) && Style.Language == FormatStyle::LK_Java) { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 51b4eee24e..cf3189a91a 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3138,6 +3138,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, PrevSpec, DiagID, Policy); isStorageClass = true; break; + case tok::kw___auto_type: + Diag(Tok, diag::ext_auto_type); + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_auto_type, Loc, PrevSpec, + DiagID, Policy); + break; case tok::kw_register: isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_register, Loc, PrevSpec, DiagID, Policy); @@ -4440,6 +4445,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { case tok::kw___private_extern__: case tok::kw_static: case tok::kw_auto: + case tok::kw___auto_type: case tok::kw_register: case tok::kw___thread: case tok::kw_thread_local: diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 4ac8f42f13..698c38f9c3 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -1127,6 +1127,7 @@ IdentifierInfo *Parser::ParseObjCSelectorPiece(SourceLocation &SelectorLoc) { case tok::kw__Bool: case tok::kw__Complex: case tok::kw___alignof: + case tok::kw___auto_type: IdentifierInfo *II = Tok.getIdentifierInfo(); SelectorLoc = ConsumeToken(); return II; diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index 9d2a2b931e..6fbcfd9bd2 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -1089,6 +1089,7 @@ public: /// [GNU] typeof-specifier /// [GNU] '_Complex' /// [C++11] 'auto' +/// [GNU] '__auto_type' /// [C++11] 'decltype' ( expression ) /// [C++1y] 'decltype' ( 'auto' ) /// @@ -1262,6 +1263,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case tok::kw_restrict: case tok::kw__Complex: case tok::kw___attribute: + case tok::kw___auto_type: return TPResult::True; // Microsoft @@ -1515,6 +1517,7 @@ bool Parser::isCXXDeclarationSpecifierAType() { case tok::kw_double: case tok::kw_void: case tok::kw___unknown_anytype: + case tok::kw___auto_type: return true; case tok::kw_auto: diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index 05ec5d4abc..498a375fe1 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -288,6 +288,7 @@ bool Declarator::isDeclarationOfFunction() const { switch (DS.getTypeSpecType()) { case TST_atomic: case TST_auto: + case TST_auto_type: case TST_bool: case TST_char: case TST_char16: @@ -476,6 +477,7 @@ const char *DeclSpec::getSpecifierName(DeclSpec::TST T, case DeclSpec::TST_typeofType: case DeclSpec::TST_typeofExpr: return "typeof"; case DeclSpec::TST_auto: return "auto"; + case DeclSpec::TST_auto_type: return "__auto_type"; case DeclSpec::TST_decltype: return "(decltype)"; case DeclSpec::TST_decltype_auto: return "decltype(auto)"; case DeclSpec::TST_underlyingType: return "__underlying_type"; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index ed86e85489..18d0c3491b 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -111,6 +111,7 @@ bool Sema::isSimpleTypeSpecifier(tok::TokenKind Kind) const { case tok::kw_wchar_t: case tok::kw_bool: case tok::kw___underlying_type: + case tok::kw___auto_type: return true; case tok::annot_typename: @@ -5765,7 +5766,10 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, NewVD = VarDecl::Create(Context, DC, D.getLocStart(), D.getIdentifierLoc(), II, R, TInfo, SC); - + + if (D.getDeclSpec().containsPlaceholderType() && R->getContainedAutoType()) + ParsingInitForAutoVars.insert(NewVD); + if (D.isInvalidType()) NewVD->setInvalidDecl(); } else { @@ -10243,7 +10247,7 @@ Sema::BuildDeclaratorGroup(MutableArrayRef Group, } else if (DeducedCanon != UCanon) { Diag(D->getTypeSourceInfo()->getTypeLoc().getBeginLoc(), diag::err_auto_different_deductions) - << (AT->isDecltypeAuto() ? 1 : 0) + << (unsigned)AT->getKeyword() << Deduced << DeducedDecl->getDeclName() << U << D->getDeclName() << DeducedDecl->getInit()->getSourceRange() diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index a1e2c0191f..aee3e3c49a 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -349,8 +349,10 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, // See if this is an auto-typed variable whose initializer we are parsing. if (ParsingInitForAutoVars.count(D)) { + const AutoType *AT = cast(D)->getType()->getContainedAutoType(); + Diag(Loc, diag::err_auto_variable_cannot_appear_in_own_initializer) - << D->getDeclName(); + << D->getDeclName() << (unsigned)AT->getKeyword(); return true; } @@ -3685,7 +3687,7 @@ static bool CheckAlignOfExpr(Sema &S, Expr *E) { return false; if (E->getObjectKind() == OK_BitField) { - S.Diag(E->getExprLoc(), diag::err_sizeof_alignof_bitfield) + S.Diag(E->getExprLoc(), diag::err_sizeof_alignof_typeof_bitfield) << 1 << E->getSourceRange(); return true; } @@ -3787,7 +3789,7 @@ Sema::CreateUnaryExprOrTypeTraitExpr(Expr *E, SourceLocation OpLoc, Diag(E->getExprLoc(), diag::err_openmp_default_simd_align_expr); isInvalid = true; } else if (E->refersToBitField()) { // C99 6.5.3.4p1. - Diag(E->getExprLoc(), diag::err_sizeof_alignof_bitfield) << 0; + Diag(E->getExprLoc(), diag::err_sizeof_alignof_typeof_bitfield) << 0; isInvalid = true; } else { isInvalid = CheckUnaryExprOrTypeTraitOperand(E, UETT_SizeOf); diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 8b2e7db8a2..2605acddfa 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -3908,7 +3908,7 @@ namespace { !Replacement.isNull() && Replacement->isDependentType(); QualType Result = SemaRef.Context.getAutoType(Dependent ? QualType() : Replacement, - TL.getTypePtr()->isDecltypeAuto(), + TL.getTypePtr()->getKeyword(), Dependent); AutoTypeLoc NewTL = TLB.push(Result); NewTL.setNameLoc(TL.getNameLoc()); @@ -3976,6 +3976,11 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result) { if (Result.isNull()) return DAR_FailedAlreadyDiagnosed; return DAR_Succeeded; + } else if (!getLangOpts().CPlusPlus) { + if (isa(Init)) { + Diag(Init->getLocStart(), diag::err_auto_init_list_from_c); + return DAR_FailedAlreadyDiagnosed; + } } } @@ -4013,6 +4018,11 @@ Sema::DeduceAutoType(TypeLoc Type, Expr *&Init, QualType &Result) { return DAR_Failed; } } else { + if (!getLangOpts().CPlusPlus && Init->refersToBitField()) { + Diag(Loc, diag::err_auto_bitfield); + return DAR_FailedAlreadyDiagnosed; + } + if (AdjustFunctionParmAndArgTypesForDeduction( *this, TemplateParamsSt.get(), FuncParam, InitType, Init, TDF)) return DAR_Failed; diff --git a/lib/Sema/SemaTemplateVariadic.cpp b/lib/Sema/SemaTemplateVariadic.cpp index a7fef8a29d..61052f06c8 100644 --- a/lib/Sema/SemaTemplateVariadic.cpp +++ b/lib/Sema/SemaTemplateVariadic.cpp @@ -737,6 +737,7 @@ bool Sema::containsUnexpandedParameterPacks(Declarator &D) { case TST_interface: case TST_class: case TST_auto: + case TST_auto_type: case TST_decltype_auto: case TST_unknown_anytype: case TST_error: diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index e53c7792e5..bbd0c3fbd0 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -1504,14 +1504,17 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { // template type parameter. Result = QualType(CorrespondingTemplateParam->getTypeForDecl(), 0); } else { - Result = Context.getAutoType(QualType(), /*decltype(auto)*/false, false); + Result = Context.getAutoType(QualType(), AutoTypeKeyword::Auto, false); } break; + case DeclSpec::TST_auto_type: + Result = Context.getAutoType(QualType(), AutoTypeKeyword::GNUAutoType, false); + break; + case DeclSpec::TST_decltype_auto: - Result = Context.getAutoType(QualType(), - /*decltype(auto)*/true, - /*IsDependent*/ false); + Result = Context.getAutoType(QualType(), AutoTypeKeyword::DecltypeAuto, + /*IsDependent*/ false); break; case DeclSpec::TST_unknown_anytype: @@ -2573,8 +2576,6 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, // The TagDecl owned by the DeclSpec. TagDecl *OwnedTagDecl = nullptr; - bool ContainsPlaceholderType = false; - switch (D.getName().getKind()) { case UnqualifiedId::IK_ImplicitSelfParam: case UnqualifiedId::IK_OperatorFunctionId: @@ -2582,7 +2583,6 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, case UnqualifiedId::IK_LiteralOperatorId: case UnqualifiedId::IK_TemplateId: T = ConvertDeclSpecToType(state); - ContainsPlaceholderType = D.getDeclSpec().containsPlaceholderType(); if (!D.isInvalidType() && D.getDeclSpec().isTypeSpecOwned()) { OwnedTagDecl = cast(D.getDeclSpec().getRepAsDecl()); @@ -2606,7 +2606,6 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, // converts to. T = SemaRef.GetTypeFromParser(D.getName().ConversionFunctionId, &ReturnTypeInfo); - ContainsPlaceholderType = T->getContainedAutoType(); break; } @@ -2614,17 +2613,10 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, distributeTypeAttrsFromDeclarator(state, T); // C++11 [dcl.spec.auto]p5: reject 'auto' if it is not in an allowed context. - // In C++11, a function declarator using 'auto' must have a trailing return - // type (this is checked later) and we can skip this. In other languages - // using auto, we need to check regardless. - // C++14 In generic lambdas allow 'auto' in their parameters. - if (ContainsPlaceholderType && - (!SemaRef.getLangOpts().CPlusPlus11 || !D.isFunctionDeclarator())) { + if (D.getDeclSpec().containsPlaceholderType()) { int Error = -1; switch (D.getContext()) { - case Declarator::KNRTypeListContext: - llvm_unreachable("K&R type lists aren't allowed in C++"); case Declarator::LambdaExprContext: llvm_unreachable("Can't specify a type specifier in lambda grammar"); case Declarator::ObjCParameterContext: @@ -2633,69 +2625,88 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, Error = 0; break; case Declarator::LambdaExprParameterContext: + // In C++14, generic lambdas allow 'auto' in their parameters. if (!(SemaRef.getLangOpts().CPlusPlus14 && D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto)) - Error = 14; + Error = 16; break; - case Declarator::MemberContext: - if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static) + case Declarator::MemberContext: { + if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static || + D.isFunctionDeclarator()) break; + bool Cxx = SemaRef.getLangOpts().CPlusPlus; switch (cast(SemaRef.CurContext)->getTagKind()) { case TTK_Enum: llvm_unreachable("unhandled tag kind"); - case TTK_Struct: Error = 1; /* Struct member */ break; - case TTK_Union: Error = 2; /* Union member */ break; - case TTK_Class: Error = 3; /* Class member */ break; - case TTK_Interface: Error = 4; /* Interface member */ break; + case TTK_Struct: Error = Cxx ? 1 : 2; /* Struct member */ break; + case TTK_Union: Error = Cxx ? 3 : 4; /* Union member */ break; + case TTK_Class: Error = 5; /* Class member */ break; + case TTK_Interface: Error = 6; /* Interface member */ break; } break; + } case Declarator::CXXCatchContext: case Declarator::ObjCCatchContext: - Error = 5; // Exception declaration + Error = 7; // Exception declaration break; case Declarator::TemplateParamContext: - Error = 6; // Template parameter + Error = 8; // Template parameter break; case Declarator::BlockLiteralContext: - Error = 7; // Block literal + Error = 9; // Block literal break; case Declarator::TemplateTypeArgContext: - Error = 8; // Template type argument + Error = 10; // Template type argument break; case Declarator::AliasDeclContext: case Declarator::AliasTemplateContext: - Error = 10; // Type alias + Error = 12; // Type alias break; case Declarator::TrailingReturnContext: - if (!SemaRef.getLangOpts().CPlusPlus14) - Error = 11; // Function return type + if (!SemaRef.getLangOpts().CPlusPlus14 || + D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto_type) + Error = 13; // Function return type break; case Declarator::ConversionIdContext: - if (!SemaRef.getLangOpts().CPlusPlus14) - Error = 12; // conversion-type-id + if (!SemaRef.getLangOpts().CPlusPlus14 || + D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto_type) + Error = 14; // conversion-type-id break; case Declarator::TypeNameContext: - Error = 13; // Generic + Error = 15; // Generic break; case Declarator::FileContext: case Declarator::BlockContext: case Declarator::ForContext: case Declarator::ConditionContext: + break; case Declarator::CXXNewContext: + if (D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto_type) + Error = 17; // 'new' type + break; + case Declarator::KNRTypeListContext: + Error = 18; // K&R function parameter break; } if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_typedef) - Error = 9; - - // In Objective-C it is an error to use 'auto' on a function declarator. - if (D.isFunctionDeclarator()) Error = 11; + // In Objective-C it is an error to use 'auto' on a function declarator + // (and everywhere for '__auto_type'). + if (D.isFunctionDeclarator() && + (!SemaRef.getLangOpts().CPlusPlus11 || + D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto_type)) + Error = 13; + + bool HaveTrailing = false; + // C++11 [dcl.spec.auto]p2: 'auto' is always fine if the declarator // contains a trailing return type. That is only legal at the outermost // level. Check all declarator chunks (outermost first) anyway, to give // better diagnostics. - if (SemaRef.getLangOpts().CPlusPlus11 && Error != -1) { + // We don't support '__auto_type' with trailing return types. + if (SemaRef.getLangOpts().CPlusPlus11 && + D.getDeclSpec().getTypeSpecType() != DeclSpec::TST_auto_type) { for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i) { unsigned chunkIndex = e - i - 1; state.setCurrentChunkIndex(chunkIndex); @@ -2703,6 +2714,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, if (DeclType.Kind == DeclaratorChunk::Function) { const DeclaratorChunk::FunctionTypeInfo &FTI = DeclType.Fun; if (FTI.hasTrailingReturnType()) { + HaveTrailing = true; Error = -1; break; } @@ -2715,16 +2727,24 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, AutoRange = D.getName().getSourceRange(); if (Error != -1) { - const bool IsDeclTypeAuto = - D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_decltype_auto; + unsigned Keyword; + switch (D.getDeclSpec().getTypeSpecType()) { + case DeclSpec::TST_auto: Keyword = 0; break; + case DeclSpec::TST_decltype_auto: Keyword = 1; break; + case DeclSpec::TST_auto_type: Keyword = 2; break; + default: llvm_unreachable("unknown auto TypeSpecType"); + } SemaRef.Diag(AutoRange.getBegin(), diag::err_auto_not_allowed) - << IsDeclTypeAuto << Error << AutoRange; + << Keyword << Error << AutoRange; T = SemaRef.Context.IntTy; D.setInvalidType(true); - } else + } else if (!HaveTrailing) { + // If there was a trailing return type, we already got + // warn_cxx98_compat_trailing_return_type in the parser. SemaRef.Diag(AutoRange.getBegin(), diag::warn_cxx98_compat_auto_type_specifier) << AutoRange; + } } if (SemaRef.getLangOpts().CPlusPlus && @@ -3729,7 +3749,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, D.setInvalidType(true); } else if (D.getContext() != Declarator::LambdaExprContext && (T.hasQualifiers() || !isa(T) || - cast(T)->isDecltypeAuto())) { + cast(T)->getKeyword() != AutoTypeKeyword::Auto)) { S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), diag::err_trailing_return_without_auto) << T << D.getDeclSpec().getSourceRange(); @@ -6792,6 +6812,9 @@ QualType Sema::BuildTypeofExprType(Expr *E, SourceLocation Loc) { if (ER.isInvalid()) return QualType(); E = ER.get(); + if (!getLangOpts().CPlusPlus && E->refersToBitField()) + Diag(E->getExprLoc(), diag::err_sizeof_alignof_typeof_bitfield) << 2; + if (!E->isTypeDependent()) { QualType T = E->getType(); if (const TagType *TT = T->getAs()) diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 12f18c7489..ca85ad5ea9 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -848,11 +848,11 @@ public: /// \brief Build a new C++11 auto type. /// /// By default, builds a new AutoType with the given deduced type. - QualType RebuildAutoType(QualType Deduced, bool IsDecltypeAuto) { + QualType RebuildAutoType(QualType Deduced, AutoTypeKeyword Keyword) { // Note, IsDependent is always false here: we implicitly convert an 'auto' // which has been deduced to a dependent type into an undeduced 'auto', so // that we'll retry deduction after the transformation. - return SemaRef.Context.getAutoType(Deduced, IsDecltypeAuto, + return SemaRef.Context.getAutoType(Deduced, Keyword, /*IsDependent*/ false); } @@ -3888,7 +3888,7 @@ TreeTransform::TransformQualifiedType(TypeLocBuilder &TLB, Qs.removeObjCLifetime(); Deduced = SemaRef.Context.getQualifiedType(Deduced.getUnqualifiedType(), Qs); - Result = SemaRef.Context.getAutoType(Deduced, AutoTy->isDecltypeAuto(), + Result = SemaRef.Context.getAutoType(Deduced, AutoTy->getKeyword(), AutoTy->isDependentType()); TLB.TypeWasModifiedSafely(Result); } else { @@ -5091,7 +5091,7 @@ QualType TreeTransform::TransformAutoType(TypeLocBuilder &TLB, QualType Result = TL.getType(); if (getDerived().AlwaysRebuild() || NewDeduced != OldDeduced || T->isDependentType()) { - Result = getDerived().RebuildAutoType(NewDeduced, T->isDecltypeAuto()); + Result = getDerived().RebuildAutoType(NewDeduced, T->getKeyword()); if (Result.isNull()) return QualType(); } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 3265603792..fa84b94b08 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -5413,9 +5413,9 @@ QualType ASTReader::readTypeRecord(unsigned Index) { case TYPE_AUTO: { QualType Deduced = readType(*Loc.F, Record, Idx); - bool IsDecltypeAuto = Record[Idx++]; + AutoTypeKeyword Keyword = (AutoTypeKeyword)Record[Idx++]; bool IsDependent = Deduced.isNull() ? Record[Idx++] : false; - return Context.getAutoType(Deduced, IsDecltypeAuto, IsDependent); + return Context.getAutoType(Deduced, Keyword, IsDependent); } case TYPE_RECORD: { diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 5508b6ecea..0b257a4a1b 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -281,7 +281,7 @@ void ASTTypeWriter::VisitUnaryTransformType(const UnaryTransformType *T) { void ASTTypeWriter::VisitAutoType(const AutoType *T) { Writer.AddTypeRef(T->getDeducedType(), Record); - Record.push_back(T->isDecltypeAuto()); + Record.push_back((unsigned)T->getKeyword()); if (T->getDeducedType().isNull()) Record.push_back(T->isDependentType()); Code = TYPE_AUTO; diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp index 39c547b9ae..eb751517e2 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp @@ -3,9 +3,9 @@ // FIXME: This is in p11 (?) in C++1y. void f() { - decltype(auto) a = a; // expected-error{{variable 'a' declared with 'auto' type cannot appear in its own initializer}} - if (decltype(auto) b = b) {} // expected-error {{variable 'b' declared with 'auto' type cannot appear in its own initializer}} - decltype(auto) c = ({ decltype(auto) d = c; 0; }); // expected-error {{variable 'c' declared with 'auto' type cannot appear in its own initializer}} + decltype(auto) a = a; // expected-error{{variable 'a' declared with 'decltype(auto)' type cannot appear in its own initializer}} + if (decltype(auto) b = b) {} // expected-error {{variable 'b' declared with 'decltype(auto)' type cannot appear in its own initializer}} + decltype(auto) c = ({ decltype(auto) d = c; 0; }); // expected-error {{variable 'c' declared with 'decltype(auto)' type cannot appear in its own initializer}} } void g() { diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-1y.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-1y.cpp index c3dc1de3ed..06bd72e125 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-1y.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p7-1y.cpp @@ -49,7 +49,7 @@ decltype(auto) f1(); decltype(auto) (*f2)(); // expected-error {{'decltype(auto)' can only be used as a return type in a function declaration}} expected-error {{requires an initializer}} decltype(auto) *f3(); // expected-error {{cannot form pointer to 'decltype(auto)'}} const decltype(auto) f4(); // expected-error {{'decltype(auto)' cannot be combined with other type specifiers}} -typedef decltype(auto) f5(); // expected-error {{'decltype(auto)' can only be used as a return type in a function declaration}} +typedef decltype(auto) f5(); // expected-error {{'decltype(auto)' not allowed in typedef}} decltype(auto) ((((((f6))))())); // ok decltype(auto) f7()(); // expected-error {{'decltype(auto)' can only be used as a return type in a function declaration}} expected-error {{function cannot return function type}} decltype(auto) (S::*f8)(); // expected-error {{'decltype(auto)' can only be used as a return type in a function declaration}} expected-error {{requires an initializer}} diff --git a/test/Sema/bitfield.c b/test/Sema/bitfield.c index ba8460d3f6..810dc798ea 100644 --- a/test/Sema/bitfield.c +++ b/test/Sema/bitfield.c @@ -63,7 +63,8 @@ typedef unsigned Unsigned; typedef signed Signed; struct Test5 { unsigned n : 2; } t5; -typedef __typeof__(t5.n) Unsigned; // Bitfield is unsigned +// Bitfield is unsigned +struct Test5 sometest5 = {-1}; // expected-warning {{implicit truncation from 'int' to bitfield changes value from -1 to 3}} typedef __typeof__(+t5.n) Signed; // ... but promotes to signed. typedef __typeof__(t5.n + 0) Signed; // Arithmetic promotes. diff --git a/test/Sema/exprs.c b/test/Sema/exprs.c index 17b1aa2851..5da4f949a1 100644 --- a/test/Sema/exprs.c +++ b/test/Sema/exprs.c @@ -97,6 +97,7 @@ int test9(struct f *P) { R = __alignof(P->x); // expected-error {{invalid application of 'alignof' to bit-field}} R = __alignof(P->y); // ok. R = sizeof(P->x); // expected-error {{invalid application of 'sizeof' to bit-field}} + __extension__ ({ R = (__typeof__(P->x)) 2; }); // expected-error {{invalid application of 'typeof' to bit-field}} return R; } diff --git a/test/SemaCXX/auto-cxx0x.cpp b/test/SemaCXX/auto-cxx0x.cpp index a8f9e84423..f3daf1a19f 100644 --- a/test/SemaCXX/auto-cxx0x.cpp +++ b/test/SemaCXX/auto-cxx0x.cpp @@ -1,5 +1,8 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++1y void f() { auto int a; // expected-warning {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} int auto b; // expected-error{{cannot combine with previous 'int' declaration specifier}} } + +typedef auto PR25449(); // expected-error {{'auto' not allowed in typedef}} diff --git a/test/SemaCXX/cxx98-compat.cpp b/test/SemaCXX/cxx98-compat.cpp index 4227272d14..25a086d9bc 100644 --- a/test/SemaCXX/cxx98-compat.cpp +++ b/test/SemaCXX/cxx98-compat.cpp @@ -100,6 +100,9 @@ struct RefQualifier { }; auto f() -> int; // expected-warning {{trailing return types are incompatible with C++98}} +#ifdef CXX14COMPAT +auto ff() { return 5; } // expected-warning {{'auto' type specifier is incompatible with C++98}} +#endif void RangeFor() { int xs[] = {1, 2, 3}; -- 2.40.0