Implement C++1y decltype(auto).

llvm-svn: 180610
This commit is contained in:
Richard Smith 2013-04-26 16:15:35 +00:00
parent af7e8c367f
commit 74aeef50a0
31 changed files with 400 additions and 92 deletions

View File

@ -1100,7 +1100,7 @@ public:
UnaryTransformType::UTTKind UKind) const; UnaryTransformType::UTTKind UKind) const;
/// \brief C++11 deduced auto type. /// \brief C++11 deduced auto type.
QualType getAutoType(QualType DeducedType) const; QualType getAutoType(QualType DeducedType, bool IsDecltypeAuto) const;
/// \brief C++11 deduction pattern for 'auto' type. /// \brief C++11 deduction pattern for 'auto' type.
QualType getAutoDeductType() const; QualType getAutoDeductType() const;

View File

@ -1326,10 +1326,20 @@ protected:
unsigned AttrKind : 32 - NumTypeBits; unsigned AttrKind : 32 - NumTypeBits;
}; };
class AutoTypeBitfields {
friend class AutoType;
unsigned : NumTypeBits;
/// Was this placeholder type spelled as 'decltype(auto)'?
unsigned IsDecltypeAuto : 1;
};
union { union {
TypeBitfields TypeBits; TypeBitfields TypeBits;
ArrayTypeBitfields ArrayTypeBits; ArrayTypeBitfields ArrayTypeBits;
AttributedTypeBitfields AttributedTypeBits; AttributedTypeBitfields AttributedTypeBits;
AutoTypeBitfields AutoTypeBits;
BuiltinTypeBitfields BuiltinTypeBits; BuiltinTypeBitfields BuiltinTypeBits;
FunctionTypeBitfields FunctionTypeBits; FunctionTypeBitfields FunctionTypeBits;
ObjCObjectTypeBitfields ObjCObjectTypeBits; ObjCObjectTypeBitfields ObjCObjectTypeBits;
@ -3542,24 +3552,27 @@ public:
} }
}; };
/// \brief Represents a C++0x auto type. /// \brief Represents a C++11 auto or C++1y decltype(auto) type.
/// ///
/// These types are usually a placeholder for a deduced type. However, within /// These types are usually a placeholder for a deduced type. However, within
/// templates and before the initializer is attached, there is no deduced type /// templates and before the initializer is attached, there is no deduced type
/// and an auto type is type-dependent and canonical. /// and an auto type is type-dependent and canonical.
class AutoType : public Type, public llvm::FoldingSetNode { class AutoType : public Type, public llvm::FoldingSetNode {
AutoType(QualType DeducedType) AutoType(QualType DeducedType, bool IsDecltypeAuto)
: Type(Auto, DeducedType.isNull() ? QualType(this, 0) : DeducedType, : Type(Auto, DeducedType.isNull() ? QualType(this, 0) : DeducedType,
/*Dependent=*/DeducedType.isNull(), /*Dependent=*/DeducedType.isNull(),
/*InstantiationDependent=*/DeducedType.isNull(), /*InstantiationDependent=*/DeducedType.isNull(),
/*VariablyModified=*/false, /*ContainsParameterPack=*/false) { /*VariablyModified=*/false, /*ContainsParameterPack=*/false) {
assert((DeducedType.isNull() || !DeducedType->isDependentType()) && assert((DeducedType.isNull() || !DeducedType->isDependentType()) &&
"deduced a dependent type for auto"); "deduced a dependent type for auto");
AutoTypeBits.IsDecltypeAuto = IsDecltypeAuto;
} }
friend class ASTContext; // ASTContext creates these friend class ASTContext; // ASTContext creates these
public: public:
bool isDecltypeAuto() const { return AutoTypeBits.IsDecltypeAuto; }
bool isSugared() const { return isDeduced(); } bool isSugared() const { return isDeduced(); }
QualType desugar() const { return getCanonicalTypeInternal(); } QualType desugar() const { return getCanonicalTypeInternal(); }
@ -3571,12 +3584,13 @@ public:
} }
void Profile(llvm::FoldingSetNodeID &ID) { void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getDeducedType()); Profile(ID, getDeducedType(), isDecltypeAuto());
} }
static void Profile(llvm::FoldingSetNodeID &ID, static void Profile(llvm::FoldingSetNodeID &ID,
QualType Deduced) { QualType Deduced, bool IsDecltypeAuto) {
ID.AddPointer(Deduced.getAsOpaquePtr()); ID.AddPointer(Deduced.getAsOpaquePtr());
ID.AddBoolean(IsDecltypeAuto);
} }
static bool classof(const Type *T) { static bool classof(const Type *T) {

View File

@ -277,6 +277,11 @@ def warn_auto_storage_class : Warning<
def ext_auto_storage_class : ExtWarn< def ext_auto_storage_class : ExtWarn<
"'auto' storage class specifier is not permitted in C++11, and will not " "'auto' storage class specifier is not permitted in C++11, and will not "
"be supported in future releases">, InGroup<DiagGroup<"auto-storage-class">>; "be supported in future releases">, InGroup<DiagGroup<"auto-storage-class">>;
def ext_decltype_auto_type_specifier : ExtWarn<
"'decltype(auto)' type specifier is a C++1y extension">, InGroup<CXX1y>;
def warn_cxx11_compat_decltype_auto_type_specifier : Warning<
"'decltype(auto)' type specifier is incompatible with C++ standards before "
"C++1y">, InGroup<CXXPre1yCompat>, DefaultIgnore;
def ext_for_range : ExtWarn< def ext_for_range : ExtWarn<
"range-based for loop is a C++11 extension">, InGroup<CXX11>; "range-based for loop is a C++11 extension">, InGroup<CXX11>;
def warn_cxx98_compat_for_range : Warning< def warn_cxx98_compat_for_range : Warning<

View File

@ -1465,6 +1465,17 @@ def warn_dangling_std_initializer_list : Warning<
"%select{the full-expression|the constructor}0">, "%select{the full-expression|the constructor}0">,
InGroup<DiagGroup<"dangling-initializer-list">>; InGroup<DiagGroup<"dangling-initializer-list">>;
// C++1y decltype(auto) type
def err_decltype_auto_cannot_be_combined : Error<
"'decltype(auto)' cannot be combined with other type specifiers">;
def err_decltype_auto_function_declarator_not_declaration : Error<
"'decltype(auto)' can only be used as a return type "
"in a function declaration">;
def err_decltype_auto_compound_type : Error<
"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">;
// C++11 override control // C++11 override control
def override_keyword_only_allowed_on_virtual_member_functions : Error< def override_keyword_only_allowed_on_virtual_member_functions : Error<
"only virtual member functions can be marked '%0'">; "only virtual member functions can be marked '%0'">;

View File

@ -38,8 +38,8 @@ namespace clang {
TST_void, TST_void,
TST_char, TST_char,
TST_wchar, // C++ wchar_t TST_wchar, // C++ wchar_t
TST_char16, // C++0x char16_t TST_char16, // C++11 char16_t
TST_char32, // C++0x char32_t TST_char32, // C++11 char32_t
TST_int, TST_int,
TST_int128, TST_int128,
TST_half, // OpenCL half, ARM NEON __fp16 TST_half, // OpenCL half, ARM NEON __fp16
@ -57,9 +57,10 @@ namespace clang {
TST_typename, // Typedef, C++ class-name or enum name, etc. TST_typename, // Typedef, C++ class-name or enum name, etc.
TST_typeofType, TST_typeofType,
TST_typeofExpr, TST_typeofExpr,
TST_decltype, // C++0x decltype TST_decltype, // C++11 decltype
TST_underlyingType, // __underlying_type for C++0x TST_underlyingType, // __underlying_type for C++11
TST_auto, // C++0x auto TST_auto, // C++11 auto
TST_decltype_auto, // C++1y decltype(auto)
TST_unknown_anytype, // __unknown_anytype extension TST_unknown_anytype, // __unknown_anytype extension
TST_atomic, // C11 _Atomic TST_atomic, // C11 _Atomic
TST_image1d_t, // OpenCL image1d_t TST_image1d_t, // OpenCL image1d_t
@ -145,7 +146,7 @@ namespace clang {
TSK_ExplicitSpecialization, TSK_ExplicitSpecialization,
/// This template specialization was instantiated from a template /// This template specialization was instantiated from a template
/// due to an explicit instantiation declaration request /// due to an explicit instantiation declaration request
/// (C++0x [temp.explicit]). /// (C++11 [temp.explicit]).
TSK_ExplicitInstantiationDeclaration, TSK_ExplicitInstantiationDeclaration,
/// This template specialization was instantiated from a template /// This template specialization was instantiated from a template
/// due to an explicit instantiation definition request /// due to an explicit instantiation definition request

View File

@ -458,19 +458,13 @@ private:
/// \brief Read an already-translated primary expression out of an annotation /// \brief Read an already-translated primary expression out of an annotation
/// token. /// token.
static ExprResult getExprAnnotation(Token &Tok) { static ExprResult getExprAnnotation(Token &Tok) {
if (Tok.getAnnotationValue()) return ExprResult::getFromOpaquePointer(Tok.getAnnotationValue());
return ExprResult((Expr *)Tok.getAnnotationValue());
return ExprResult(true);
} }
/// \brief Set the primary expression corresponding to the given annotation /// \brief Set the primary expression corresponding to the given annotation
/// token. /// token.
static void setExprAnnotation(Token &Tok, ExprResult ER) { static void setExprAnnotation(Token &Tok, ExprResult ER) {
if (ER.isInvalid()) Tok.setAnnotationValue(ER.getAsOpaquePointer());
Tok.setAnnotationValue(0);
else
Tok.setAnnotationValue(ER.get());
} }
public: public:

View File

@ -285,6 +285,7 @@ public:
static const TST TST_typeofType = clang::TST_typeofType; static const TST TST_typeofType = clang::TST_typeofType;
static const TST TST_typeofExpr = clang::TST_typeofExpr; static const TST TST_typeofExpr = clang::TST_typeofExpr;
static const TST TST_decltype = clang::TST_decltype; static const TST TST_decltype = clang::TST_decltype;
static const TST TST_decltype_auto = clang::TST_decltype_auto;
static const TST TST_underlyingType = clang::TST_underlyingType; static const TST TST_underlyingType = clang::TST_underlyingType;
static const TST TST_auto = clang::TST_auto; static const TST TST_auto = clang::TST_auto;
static const TST TST_unknown_anytype = clang::TST_unknown_anytype; static const TST TST_unknown_anytype = clang::TST_unknown_anytype;
@ -504,6 +505,10 @@ public:
SourceRange getTypeofParensRange() const { return TypeofParensRange; } SourceRange getTypeofParensRange() const { return TypeofParensRange; }
void setTypeofParensRange(SourceRange range) { TypeofParensRange = range; } void setTypeofParensRange(SourceRange range) { TypeofParensRange = range; }
bool containsPlaceholderType() const {
return TypeSpecType == TST_auto || TypeSpecType == TST_decltype_auto;
}
/// \brief Turn a type-specifier-type into a string like "_Bool" or "union". /// \brief Turn a type-specifier-type into a string like "_Bool" or "union".
static const char *getSpecifierName(DeclSpec::TST T); static const char *getSpecifierName(DeclSpec::TST T);
static const char *getSpecifierName(DeclSpec::TQ Q); static const char *getSpecifierName(DeclSpec::TQ Q);

View File

@ -207,6 +207,15 @@ namespace clang {
assert((PtrWithInvalid & 0x01) == 0 && "Badly aligned pointer"); assert((PtrWithInvalid & 0x01) == 0 && "Badly aligned pointer");
return *this; return *this;
} }
// For types where we can fit a flag in with the pointer, provide
// conversions to/from pointer type.
static ActionResult getFromOpaquePointer(void *P) {
ActionResult Result;
Result.PtrWithInvalid = (uintptr_t)P;
return Result;
}
void *getAsOpaquePointer() const { return (void*)PtrWithInvalid; }
}; };
/// An opaque type for threading parsed type information through the /// An opaque type for threading parsed type information through the

View File

@ -3516,17 +3516,19 @@ QualType ASTContext::getUnaryTransformType(QualType BaseType,
} }
/// getAutoType - We only unique auto types after they've been deduced. /// getAutoType - We only unique auto types after they've been deduced.
QualType ASTContext::getAutoType(QualType DeducedType) const { QualType ASTContext::getAutoType(QualType DeducedType,
bool IsDecltypeAuto) const {
void *InsertPos = 0; void *InsertPos = 0;
if (!DeducedType.isNull()) { if (!DeducedType.isNull()) {
// Look in the folding set for an existing type. // Look in the folding set for an existing type.
llvm::FoldingSetNodeID ID; llvm::FoldingSetNodeID ID;
AutoType::Profile(ID, DeducedType); AutoType::Profile(ID, DeducedType, IsDecltypeAuto);
if (AutoType *AT = AutoTypes.FindNodeOrInsertPos(ID, InsertPos)) if (AutoType *AT = AutoTypes.FindNodeOrInsertPos(ID, InsertPos))
return QualType(AT, 0); return QualType(AT, 0);
} }
AutoType *AT = new (*this, TypeAlignment) AutoType(DeducedType); AutoType *AT = new (*this, TypeAlignment) AutoType(DeducedType,
IsDecltypeAuto);
Types.push_back(AT); Types.push_back(AT);
if (InsertPos) if (InsertPos)
AutoTypes.InsertNode(AT, InsertPos); AutoTypes.InsertNode(AT, InsertPos);
@ -3564,7 +3566,7 @@ QualType ASTContext::getAtomicType(QualType T) const {
/// getAutoDeductType - Get type pattern for deducing against 'auto'. /// getAutoDeductType - Get type pattern for deducing against 'auto'.
QualType ASTContext::getAutoDeductType() const { QualType ASTContext::getAutoDeductType() const {
if (AutoDeductTy.isNull()) if (AutoDeductTy.isNull())
AutoDeductTy = getAutoType(QualType()); AutoDeductTy = getAutoType(QualType(), false);
assert(!AutoDeductTy.isNull() && "can't build 'auto' pattern"); assert(!AutoDeductTy.isNull() && "can't build 'auto' pattern");
return AutoDeductTy; return AutoDeductTy;
} }

View File

@ -1680,7 +1680,7 @@ QualType ASTNodeImporter::VisitUnaryTransformType(const UnaryTransformType *T) {
} }
QualType ASTNodeImporter::VisitAutoType(const AutoType *T) { QualType ASTNodeImporter::VisitAutoType(const AutoType *T) {
// FIXME: Make sure that the "to" context supports C++0x! // FIXME: Make sure that the "to" context supports C++11!
QualType FromDeduced = T->getDeducedType(); QualType FromDeduced = T->getDeducedType();
QualType ToDeduced; QualType ToDeduced;
if (!FromDeduced.isNull()) { if (!FromDeduced.isNull()) {
@ -1689,7 +1689,7 @@ QualType ASTNodeImporter::VisitAutoType(const AutoType *T) {
return QualType(); return QualType();
} }
return Importer.getToContext().getAutoType(ToDeduced); return Importer.getToContext().getAutoType(ToDeduced, T->isDecltypeAuto());
} }
QualType ASTNodeImporter::VisitRecordType(const RecordType *T) { QualType ASTNodeImporter::VisitRecordType(const RecordType *T) {

View File

@ -2275,7 +2275,7 @@ void CXXNameMangler::mangleType(const AutoType *T) {
QualType D = T->getDeducedType(); QualType D = T->getDeducedType();
// <builtin-type> ::= Da # dependent auto // <builtin-type> ::= Da # dependent auto
if (D.isNull()) if (D.isNull())
Out << "Da"; Out << (T->isDecltypeAuto() ? "Dc" : "Da");
else else
mangleType(D); mangleType(D);
} }

View File

@ -779,7 +779,7 @@ void TypePrinter::printAutoBefore(const AutoType *T, raw_ostream &OS) {
if (T->isDeduced()) { if (T->isDeduced()) {
printBefore(T->getDeducedType(), OS); printBefore(T->getDeducedType(), OS);
} else { } else {
OS << "auto"; OS << (T->isDecltypeAuto() ? "decltype(auto)" : "auto");
spaceBeforePlaceHolder(OS); spaceBeforePlaceHolder(OS);
} }
} }

View File

@ -57,8 +57,7 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS,
if (FnD) { if (FnD) {
Actions.ProcessDeclAttributeList(getCurScope(), FnD, AccessAttrs, Actions.ProcessDeclAttributeList(getCurScope(), FnD, AccessAttrs,
false, true); false, true);
bool TypeSpecContainsAuto bool TypeSpecContainsAuto = D.getDeclSpec().containsPlaceholderType();
= D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto;
if (Init.isUsable()) if (Init.isUsable())
Actions.AddInitializerToDecl(FnD, Init.get(), false, Actions.AddInitializerToDecl(FnD, Init.get(), false,
TypeSpecContainsAuto); TypeSpecContainsAuto);

View File

@ -1809,8 +1809,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D,
} }
} }
bool TypeContainsAuto = bool TypeContainsAuto = D.getDeclSpec().containsPlaceholderType();
D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto;
// Parse declarator '=' initializer. // Parse declarator '=' initializer.
// If a '==' or '+=' is found, suggest a fixit to '='. // If a '==' or '+=' is found, suggest a fixit to '='.

View File

@ -674,15 +674,15 @@ Decl *Parser::ParseStaticAssertDeclaration(SourceLocation &DeclEnd){
T.getCloseLocation()); T.getCloseLocation());
} }
/// ParseDecltypeSpecifier - Parse a C++0x decltype specifier. /// ParseDecltypeSpecifier - Parse a C++11 decltype specifier.
/// ///
/// 'decltype' ( expression ) /// 'decltype' ( expression )
/// 'decltype' ( 'auto' ) [C++1y]
/// ///
SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) { SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
assert((Tok.is(tok::kw_decltype) || Tok.is(tok::annot_decltype)) assert((Tok.is(tok::kw_decltype) || Tok.is(tok::annot_decltype))
&& "Not a decltype specifier"); && "Not a decltype specifier");
ExprResult Result; ExprResult Result;
SourceLocation StartLoc = Tok.getLocation(); SourceLocation StartLoc = Tok.getLocation();
SourceLocation EndLoc; SourceLocation EndLoc;
@ -709,16 +709,28 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
StartLoc : T.getOpenLocation(); StartLoc : T.getOpenLocation();
} }
// 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'.
Diag(Tok.getLocation(),
getLangOpts().CPlusPlus1y
? diag::warn_cxx11_compat_decltype_auto_type_specifier
: diag::ext_decltype_auto_type_specifier);
ConsumeToken();
} else {
// Parse the expression // Parse the expression
// C++0x [dcl.type.simple]p4: // C++11 [dcl.type.simple]p4:
// The operand of the decltype specifier is an unevaluated operand. // The operand of the decltype specifier is an unevaluated operand.
EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated, EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated,
0, /*IsDecltype=*/true); 0, /*IsDecltype=*/true);
Result = ParseExpression(); Result = ParseExpression();
if (Result.isInvalid()) { if (Result.isInvalid()) {
DS.SetTypeSpecError(); DS.SetTypeSpecError();
if (SkipUntil(tok::r_paren, /*StopAtSemi=*/true, /*DontConsume=*/true)) { if (SkipUntil(tok::r_paren, /*StopAtSemi=*/true,
/*DontConsume=*/true)) {
EndLoc = ConsumeParen(); EndLoc = ConsumeParen();
} else { } else {
if (PP.isBacktrackEnabled() && Tok.is(tok::semi)) { if (PP.isBacktrackEnabled() && Tok.is(tok::semi)) {
@ -734,6 +746,9 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
return EndLoc; return EndLoc;
} }
Result = Actions.ActOnDecltypeExpression(Result.take());
}
// Match the ')' // Match the ')'
T.consumeClose(); T.consumeClose();
if (T.getCloseLocation().isInvalid()) { if (T.getCloseLocation().isInvalid()) {
@ -743,7 +758,6 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
return T.getCloseLocation(); return T.getCloseLocation();
} }
Result = Actions.ActOnDecltypeExpression(Result.take());
if (Result.isInvalid()) { if (Result.isInvalid()) {
DS.SetTypeSpecError(); DS.SetTypeSpecError();
return T.getCloseLocation(); return T.getCloseLocation();
@ -751,12 +765,16 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
EndLoc = T.getCloseLocation(); EndLoc = T.getCloseLocation();
} }
assert(!Result.isInvalid());
const char *PrevSpec = 0; const char *PrevSpec = 0;
unsigned DiagID; unsigned DiagID;
// Check for duplicate type specifiers (e.g. "int decltype(a)"). // Check for duplicate type specifiers (e.g. "int decltype(a)").
if (DS.SetTypeSpecType(DeclSpec::TST_decltype, StartLoc, PrevSpec, if (Result.get()
DiagID, Result.release())) { ? DS.SetTypeSpecType(DeclSpec::TST_decltype, StartLoc, PrevSpec,
DiagID, Result.release())
: DS.SetTypeSpecType(DeclSpec::TST_decltype_auto, StartLoc, PrevSpec,
DiagID)) {
Diag(StartLoc, DiagID) << PrevSpec; Diag(StartLoc, DiagID) << PrevSpec;
DS.SetTypeSpecError(); DS.SetTypeSpecError();
} }
@ -773,8 +791,10 @@ void Parser::AnnotateExistingDecltypeSpecifier(const DeclSpec& DS,
PP.EnterToken(Tok); PP.EnterToken(Tok);
Tok.setKind(tok::annot_decltype); Tok.setKind(tok::annot_decltype);
setExprAnnotation(Tok, DS.getTypeSpecType() == TST_decltype ? setExprAnnotation(Tok,
DS.getRepAsExpr() : ExprResult()); DS.getTypeSpecType() == TST_decltype ? DS.getRepAsExpr() :
DS.getTypeSpecType() == TST_decltype_auto ? ExprResult() :
ExprError());
Tok.setAnnotationEndLoc(EndLoc); Tok.setAnnotationEndLoc(EndLoc);
Tok.setLocation(StartLoc); Tok.setLocation(StartLoc);
PP.AnnotateCachedTokens(Tok); PP.AnnotateCachedTokens(Tok);
@ -2267,11 +2287,10 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
SkipUntil(tok::comma, true, true); SkipUntil(tok::comma, true, true);
else if (ThisDecl) else if (ThisDecl)
Actions.AddInitializerToDecl(ThisDecl, Init.get(), EqualLoc.isInvalid(), Actions.AddInitializerToDecl(ThisDecl, Init.get(), EqualLoc.isInvalid(),
DS.getTypeSpecType() == DeclSpec::TST_auto); DS.containsPlaceholderType());
} else if (ThisDecl && DS.getStorageClassSpec() == DeclSpec::SCS_static) { } else if (ThisDecl && DS.getStorageClassSpec() == DeclSpec::SCS_static) {
// No initializer. // No initializer.
Actions.ActOnUninitializedDecl(ThisDecl, Actions.ActOnUninitializedDecl(ThisDecl, DS.containsPlaceholderType());
DS.getTypeSpecType() == DeclSpec::TST_auto);
} }
if (ThisDecl) { if (ThisDecl) {

View File

@ -693,6 +693,12 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
break; break;
case tok::kw_decltype: case tok::kw_decltype:
// Annotate the token and tail recurse.
if (TryAnnotateTypeOrScopeToken())
return ExprError();
assert(Tok.isNot(tok::kw_decltype));
return ParseCastExpression(isUnaryExpression, isAddressOfOperand);
case tok::identifier: { // primary-expression: identifier case tok::identifier: { // primary-expression: identifier
// unqualified-id: identifier // unqualified-id: identifier
// constant: enumeration-constant // constant: enumeration-constant

View File

@ -1455,7 +1455,7 @@ bool Parser::ParseCXXCondition(ExprResult &ExprOut,
if (!InitExpr.isInvalid()) if (!InitExpr.isInvalid())
Actions.AddInitializerToDecl(DeclOut, InitExpr.take(), !CopyInitialization, Actions.AddInitializerToDecl(DeclOut, InitExpr.take(), !CopyInitialization,
DS.getTypeSpecType() == DeclSpec::TST_auto); DS.containsPlaceholderType());
// FIXME: Build a reference to this declaration? Convert it to bool? // FIXME: Build a reference to this declaration? Convert it to bool?
// (This is currently handled by Sema). // (This is currently handled by Sema).

View File

@ -942,6 +942,7 @@ bool Parser::isTentativelyDeclared(IdentifierInfo *II) {
/// [GNU] '_Complex' /// [GNU] '_Complex'
/// [C++11] 'auto' /// [C++11] 'auto'
/// [C++11] 'decltype' ( expression ) /// [C++11] 'decltype' ( expression )
/// [C++1y] 'decltype' ( 'auto' )
/// ///
/// type-name: /// type-name:
/// class-name /// class-name

View File

@ -294,6 +294,11 @@ bool Declarator::isDeclarationOfFunction() const {
case TST_event_t: case TST_event_t:
return false; return false;
case TST_decltype_auto:
// This must have an initializer, so can't be a function declaration,
// even if the initializer has function type.
return false;
case TST_decltype: case TST_decltype:
case TST_typeofExpr: case TST_typeofExpr:
if (Expr *E = DS.getRepAsExpr()) if (Expr *E = DS.getRepAsExpr())
@ -434,6 +439,7 @@ const char *DeclSpec::getSpecifierName(DeclSpec::TST T) {
case DeclSpec::TST_typeofExpr: return "typeof"; case DeclSpec::TST_typeofExpr: return "typeof";
case DeclSpec::TST_auto: return "auto"; case DeclSpec::TST_auto: return "auto";
case DeclSpec::TST_decltype: return "(decltype)"; case DeclSpec::TST_decltype: return "(decltype)";
case DeclSpec::TST_decltype_auto: return "decltype(auto)";
case DeclSpec::TST_underlyingType: return "__underlying_type"; case DeclSpec::TST_underlyingType: return "__underlying_type";
case DeclSpec::TST_unknown_anytype: return "__unknown_anytype"; case DeclSpec::TST_unknown_anytype: return "__unknown_anytype";
case DeclSpec::TST_atomic: return "_Atomic"; case DeclSpec::TST_atomic: return "_Atomic";
@ -494,7 +500,7 @@ bool DeclSpec::SetStorageClassSpec(Sema &S, SCS SC, SourceLocation Loc,
} }
if (StorageClassSpec != SCS_unspecified) { if (StorageClassSpec != SCS_unspecified) {
// Maybe this is an attempt to use C++0x 'auto' outside of C++0x mode. // Maybe this is an attempt to use C++11 'auto' outside of C++11 mode.
bool isInvalid = true; bool isInvalid = true;
if (TypeSpecType == TST_unspecified && S.getLangOpts().CPlusPlus) { if (TypeSpecType == TST_unspecified && S.getLangOpts().CPlusPlus) {
if (SC == SCS_auto) if (SC == SCS_auto)
@ -834,6 +840,39 @@ void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP) {
// Check the type specifier components first. // Check the type specifier components first.
// If decltype(auto) is used, no other type specifiers are permitted.
if (TypeSpecType == TST_decltype_auto &&
(TypeSpecWidth != TSW_unspecified ||
TypeSpecComplex != TSC_unspecified ||
TypeSpecSign != TSS_unspecified ||
TypeAltiVecVector || TypeAltiVecPixel || TypeAltiVecBool ||
TypeQualifiers)) {
const int NumLocs = 8;
SourceLocation ExtraLocs[NumLocs] = {
TSWLoc, TSCLoc, TSSLoc, AltiVecLoc,
TQ_constLoc, TQ_restrictLoc, TQ_volatileLoc, TQ_atomicLoc
};
FixItHint Hints[NumLocs];
SourceLocation FirstLoc;
for (unsigned I = 0; I != NumLocs; ++I) {
if (!ExtraLocs[I].isInvalid()) {
if (FirstLoc.isInvalid() ||
PP.getSourceManager().isBeforeInTranslationUnit(ExtraLocs[I],
FirstLoc))
FirstLoc = ExtraLocs[I];
Hints[I] = FixItHint::CreateRemoval(ExtraLocs[I]);
}
}
TypeSpecWidth = TSW_unspecified;
TypeSpecComplex = TSC_unspecified;
TypeSpecSign = TSS_unspecified;
TypeAltiVecVector = TypeAltiVecPixel = TypeAltiVecBool = false;
TypeQualifiers = 0;
Diag(D, TSTLoc, diag::err_decltype_auto_cannot_be_combined)
<< Hints[0] << Hints[1] << Hints[2] << Hints[3]
<< Hints[4] << Hints[5] << Hints[6] << Hints[7];
}
// Validate and finalize AltiVec vector declspec. // Validate and finalize AltiVec vector declspec.
if (TypeAltiVecVector) { if (TypeAltiVecVector) {
if (TypeAltiVecBool) { if (TypeAltiVecBool) {
@ -973,7 +1012,7 @@ void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP) {
StorageClassSpecLoc = SourceLocation(); StorageClassSpecLoc = SourceLocation();
} }
// Diagnose if we've recovered from an ill-formed 'auto' storage class // Diagnose if we've recovered from an ill-formed 'auto' storage class
// specifier in a pre-C++0x dialect of C++. // specifier in a pre-C++11 dialect of C++.
if (!PP.getLangOpts().CPlusPlus11 && TypeSpecType == TST_auto) if (!PP.getLangOpts().CPlusPlus11 && TypeSpecType == TST_auto)
Diag(D, TSTLoc, diag::ext_auto_type_specifier); Diag(D, TSTLoc, diag::ext_auto_type_specifier);
if (PP.getLangOpts().CPlusPlus && !PP.getLangOpts().CPlusPlus11 && if (PP.getLangOpts().CPlusPlus && !PP.getLangOpts().CPlusPlus11 &&

View File

@ -4844,8 +4844,7 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC,
// If this decl has an auto type in need of deduction, make a note of the // If this decl has an auto type in need of deduction, make a note of the
// Decl so we can diagnose uses of it in its own initializer. // Decl so we can diagnose uses of it in its own initializer.
if (D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto && if (D.getDeclSpec().containsPlaceholderType() && R->getContainedAutoType())
R->getContainedAutoType())
ParsingInitForAutoVars.insert(NewVD); ParsingInitForAutoVars.insert(NewVD);
if (D.isInvalidType() || Invalid) if (D.isInvalidType() || Invalid)
@ -8166,7 +8165,7 @@ Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS,
getASTContext().addUnnamedTag(Tag); getASTContext().addUnnamedTag(Tag);
return BuildDeclaratorGroup(Decls.data(), Decls.size(), return BuildDeclaratorGroup(Decls.data(), Decls.size(),
DS.getTypeSpecType() == DeclSpec::TST_auto); DS.containsPlaceholderType());
} }
/// BuildDeclaratorGroup - convert a list of declarations into a declaration /// BuildDeclaratorGroup - convert a list of declarations into a declaration

View File

@ -996,7 +996,7 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal,
SourceLocation PlacementLParen, MultiExprArg PlacementArgs, SourceLocation PlacementLParen, MultiExprArg PlacementArgs,
SourceLocation PlacementRParen, SourceRange TypeIdParens, SourceLocation PlacementRParen, SourceRange TypeIdParens,
Declarator &D, Expr *Initializer) { Declarator &D, Expr *Initializer) {
bool TypeContainsAuto = D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto; bool TypeContainsAuto = D.getDeclSpec().containsPlaceholderType();
Expr *ArraySize = 0; Expr *ArraySize = 0;
// If the specified type is an array, unwrap it and save the expression. // If the specified type is an array, unwrap it and save the expression.

View File

@ -3579,11 +3579,13 @@ namespace {
// auto type deduced as T" in order for [temp.deduct.call]p3 to apply. // auto type deduced as T" in order for [temp.deduct.call]p3 to apply.
if (isa<TemplateTypeParmType>(Replacement)) { if (isa<TemplateTypeParmType>(Replacement)) {
QualType Result = Replacement; QualType Result = Replacement;
TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result); TemplateTypeParmTypeLoc NewTL =
TLB.push<TemplateTypeParmTypeLoc>(Result);
NewTL.setNameLoc(TL.getNameLoc()); NewTL.setNameLoc(TL.getNameLoc());
return Result; return Result;
} else { } else {
QualType Result = RebuildAutoType(Replacement); QualType Result = RebuildAutoType(Replacement,
TL.getTypePtr()->isDecltypeAuto());
AutoTypeLoc NewTL = TLB.push<AutoTypeLoc>(Result); AutoTypeLoc NewTL = TLB.push<AutoTypeLoc>(Result);
NewTL.setNameLoc(TL.getNameLoc()); NewTL.setNameLoc(TL.getNameLoc());
return Result; return Result;
@ -3657,6 +3659,24 @@ Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init,
return DAR_Succeeded; return DAR_Succeeded;
} }
// If this is a 'decltype(auto)' specifier, do the decltype dance.
// Since 'decltype(auto)' can only occur at the top of the type, we
// don't need to go digging for it.
if (const AutoType *AT = Type->getType()->getAs<AutoType>()) {
if (AT->isDecltypeAuto()) {
if (isa<InitListExpr>(Init)) {
Diag(Init->getLocStart(), diag::err_decltype_auto_initializer_list);
return DAR_FailedAlreadyDiagnosed;
}
QualType Deduced = BuildDecltypeType(Init, Init->getLocStart());
// FIXME: Support a non-canonical deduced type for 'auto'.
Deduced = Context.getCanonicalType(Deduced);
Result = SubstituteAutoTransform(*this, Deduced).TransformType(Type);
return DAR_Succeeded;
}
}
SourceLocation Loc = Init->getExprLoc(); SourceLocation Loc = Init->getExprLoc();
LocalInstantiationScope InstScope(*this); LocalInstantiationScope InstScope(*this);

View File

@ -727,6 +727,7 @@ bool Sema::containsUnexpandedParameterPacks(Declarator &D) {
case TST_interface: case TST_interface:
case TST_class: case TST_class:
case TST_auto: case TST_auto:
case TST_decltype_auto:
case TST_unknown_anytype: case TST_unknown_anytype:
case TST_image1d_t: case TST_image1d_t:
case TST_image1d_array_t: case TST_image1d_array_t:

View File

@ -994,11 +994,14 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
} }
break; break;
case DeclSpec::TST_auto: { case DeclSpec::TST_auto:
// TypeQuals handled by caller. // TypeQuals handled by caller.
Result = Context.getAutoType(QualType()); Result = Context.getAutoType(QualType(), /*decltype(auto)*/false);
break;
case DeclSpec::TST_decltype_auto:
Result = Context.getAutoType(QualType(), /*decltype(auto)*/true);
break; break;
}
case DeclSpec::TST_unknown_anytype: case DeclSpec::TST_unknown_anytype:
Result = Context.UnknownAnyTy; Result = Context.UnknownAnyTy;
@ -1457,12 +1460,6 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM,
return QualType(); return QualType();
} }
if (T->getContainedAutoType()) {
Diag(Loc, diag::err_illegal_decl_array_of_auto)
<< getPrintableNameForEntity(Entity) << T;
return QualType();
}
if (const RecordType *EltTy = T->getAs<RecordType>()) { if (const RecordType *EltTy = T->getAs<RecordType>()) {
// If the element type is a struct or union that contains a variadic // If the element type is a struct or union that contains a variadic
// array, accept it as a GNU extension: C99 6.7.2.1p2. // array, accept it as a GNU extension: C99 6.7.2.1p2.
@ -2063,7 +2060,7 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
// In C++11, a function declarator using 'auto' must have a trailing return // 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 // type (this is checked later) and we can skip this. In other languages
// using auto, we need to check regardless. // using auto, we need to check regardless.
if (D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto && if (D.getDeclSpec().containsPlaceholderType() &&
(!SemaRef.getLangOpts().CPlusPlus11 || !D.isFunctionDeclarator())) { (!SemaRef.getLangOpts().CPlusPlus11 || !D.isFunctionDeclarator())) {
int Error = -1; int Error = -1;
@ -2402,6 +2399,46 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
(T->castAs<FunctionProtoType>()->getTypeQuals() != 0 || (T->castAs<FunctionProtoType>()->getTypeQuals() != 0 ||
T->castAs<FunctionProtoType>()->getRefQualifier() != RQ_None); T->castAs<FunctionProtoType>()->getRefQualifier() != RQ_None);
// If T is 'decltype(auto)', the only declarators we can have are parens
// and at most one function declarator if this is a function declaration.
if (const AutoType *AT = T->getAs<AutoType>()) {
if (AT->isDecltypeAuto()) {
for (unsigned I = 0, E = D.getNumTypeObjects(); I != E; ++I) {
unsigned Index = E - I - 1;
DeclaratorChunk &DeclChunk = D.getTypeObject(Index);
unsigned DiagId = diag::err_decltype_auto_compound_type;
unsigned DiagKind = 0;
switch (DeclChunk.Kind) {
case DeclaratorChunk::Paren:
continue;
case DeclaratorChunk::Function: {
unsigned FnIndex;
if (D.isFunctionDeclarationContext() &&
D.isFunctionDeclarator(FnIndex) && FnIndex == Index)
continue;
DiagId = diag::err_decltype_auto_function_declarator_not_declaration;
break;
}
case DeclaratorChunk::Pointer:
case DeclaratorChunk::BlockPointer:
case DeclaratorChunk::MemberPointer:
DiagKind = 0;
break;
case DeclaratorChunk::Reference:
DiagKind = 1;
break;
case DeclaratorChunk::Array:
DiagKind = 2;
break;
}
S.Diag(DeclChunk.Loc, DiagId) << DiagKind;
D.setInvalidType(true);
break;
}
}
}
// Walk the DeclTypeInfo, building the recursive type as we go. // Walk the DeclTypeInfo, building the recursive type as we go.
// DeclTypeInfos are ordered from the identifier out, which is // DeclTypeInfos are ordered from the identifier out, which is
// opposite of what we want :). // opposite of what we want :).
@ -2530,6 +2567,15 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
} }
} }
if (const AutoType *AT = T->getContainedAutoType()) {
// We've already diagnosed this for decltype(auto).
if (!AT->isDecltypeAuto()) {
S.Diag(DeclType.Loc, diag::err_illegal_decl_array_of_auto)
<< getPrintableNameForEntity(Name) << T;
D.setInvalidType(true);
}
}
T = S.BuildArrayType(T, ASM, ArraySize, ATI.TypeQuals, T = S.BuildArrayType(T, ASM, ArraySize, ATI.TypeQuals,
SourceRange(DeclType.Loc, DeclType.EndLoc), Name); SourceRange(DeclType.Loc, DeclType.EndLoc), Name);
break; break;
@ -2560,7 +2606,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
<< T << D.getDeclSpec().getSourceRange(); << T << D.getDeclSpec().getSourceRange();
D.setInvalidType(true); D.setInvalidType(true);
} else if (D.getContext() != Declarator::LambdaExprContext && } else if (D.getContext() != Declarator::LambdaExprContext &&
(T.hasQualifiers() || !isa<AutoType>(T))) { (T.hasQualifiers() || !isa<AutoType>(T) ||
cast<AutoType>(T)->isDecltypeAuto())) {
S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(),
diag::err_trailing_return_without_auto) diag::err_trailing_return_without_auto)
<< T << D.getDeclSpec().getSourceRange(); << T << D.getDeclSpec().getSourceRange();

View File

@ -754,17 +754,17 @@ public:
UnaryTransformType::UTTKind UKind, UnaryTransformType::UTTKind UKind,
SourceLocation Loc); SourceLocation Loc);
/// \brief Build a new C++0x decltype type. /// \brief Build a new C++11 decltype type.
/// ///
/// By default, performs semantic analysis when building the decltype type. /// By default, performs semantic analysis when building the decltype type.
/// Subclasses may override this routine to provide different behavior. /// Subclasses may override this routine to provide different behavior.
QualType RebuildDecltypeType(Expr *Underlying, SourceLocation Loc); QualType RebuildDecltypeType(Expr *Underlying, SourceLocation Loc);
/// \brief Build a new C++0x auto type. /// \brief Build a new C++11 auto type.
/// ///
/// By default, builds a new AutoType with the given deduced type. /// By default, builds a new AutoType with the given deduced type.
QualType RebuildAutoType(QualType Deduced) { QualType RebuildAutoType(QualType Deduced, bool IsDecltypeAuto) {
return SemaRef.Context.getAutoType(Deduced); return SemaRef.Context.getAutoType(Deduced, IsDecltypeAuto);
} }
/// \brief Build a new template specialization type. /// \brief Build a new template specialization type.
@ -3407,7 +3407,7 @@ TreeTransform<Derived>::TransformQualifiedType(TypeLocBuilder &TLB,
Qs.removeObjCLifetime(); Qs.removeObjCLifetime();
Deduced = SemaRef.Context.getQualifiedType(Deduced.getUnqualifiedType(), Deduced = SemaRef.Context.getQualifiedType(Deduced.getUnqualifiedType(),
Qs); Qs);
Result = SemaRef.Context.getAutoType(Deduced); Result = SemaRef.Context.getAutoType(Deduced, AutoTy->isDecltypeAuto());
TLB.TypeWasModifiedSafely(Result); TLB.TypeWasModifiedSafely(Result);
} else { } else {
// Otherwise, complain about the addition of a qualifier to an // Otherwise, complain about the addition of a qualifier to an
@ -4501,7 +4501,7 @@ QualType TreeTransform<Derived>::TransformAutoType(TypeLocBuilder &TLB,
QualType Result = TL.getType(); QualType Result = TL.getType();
if (getDerived().AlwaysRebuild() || NewDeduced != OldDeduced) { if (getDerived().AlwaysRebuild() || NewDeduced != OldDeduced) {
Result = getDerived().RebuildAutoType(NewDeduced); Result = getDerived().RebuildAutoType(NewDeduced, T->isDecltypeAuto());
if (Result.isNull()) if (Result.isNull())
return QualType(); return QualType();
} }

View File

@ -4626,8 +4626,11 @@ QualType ASTReader::readTypeRecord(unsigned Index) {
return Context.getUnaryTransformType(BaseType, UnderlyingType, UKind); return Context.getUnaryTransformType(BaseType, UnderlyingType, UKind);
} }
case TYPE_AUTO: case TYPE_AUTO: {
return Context.getAutoType(readType(*Loc.F, Record, Idx)); QualType Deduced = readType(*Loc.F, Record, Idx);
bool IsDecltypeAuto = Record[Idx++];
return Context.getAutoType(Deduced, IsDecltypeAuto);
}
case TYPE_RECORD: { case TYPE_RECORD: {
if (Record.size() != 2) { if (Record.size() != 2) {

View File

@ -245,6 +245,7 @@ void ASTTypeWriter::VisitUnaryTransformType(const UnaryTransformType *T) {
void ASTTypeWriter::VisitAutoType(const AutoType *T) { void ASTTypeWriter::VisitAutoType(const AutoType *T) {
Writer.AddTypeRef(T->getDeducedType(), Record); Writer.AddTypeRef(T->getDeducedType(), Record);
Record.push_back(T->isDecltypeAuto());
Code = TYPE_AUTO; Code = TYPE_AUTO;
} }

View File

@ -0,0 +1,43 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++1y
// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 -Wno-c++1y-extensions
// 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}}
}
void g() {
decltype(auto) a; // expected-error{{declaration of variable 'a' with type 'decltype(auto)' requires an initializer}}
decltype(auto) *b; // expected-error{{cannot form pointer to 'decltype(auto)'}} expected-error{{declaration of variable 'b' with type 'decltype(auto) *' requires an initializer}}
if (decltype(auto) b) {} // expected-error {{must have an initializer}}
for (;decltype(auto) b;) {} // expected-error {{must have an initializer}}
while (decltype(auto) b) {} // expected-error {{must have an initializer}}
if (decltype(auto) b = true) { (void)b; }
}
decltype(auto) n(1,2,3); // expected-error{{initializer for variable 'n' with type 'decltype(auto)' contains multiple expressions}}
namespace N
{
// All of these are references, because a string literal is an lvalue.
decltype(auto) a = "const char (&)[19]", b = a, c = (a);
}
void h() {
decltype(auto) b = 42ULL;
for (decltype(auto) c = 0; c < b; ++c) {
}
}
template<typename T, typename U> struct same;
template<typename T> struct same<T, T> {};
void i() {
decltype(auto) x = 5;
decltype(auto) int r; // expected-error {{cannot combine with previous 'decltype(auto)' declaration specifier}} expected-error {{requires an initializer}}
}

View File

@ -0,0 +1,62 @@
// RUN: %clang_cc1 -verify -std=c++1y %s
namespace std {
template<typename T> struct initializer_list {
const T *p;
unsigned long n;
initializer_list(const T *p, unsigned long n);
};
}
// FIXME: This may not be p6 in C++1y; N3638 isn't very clear whether paragraphs
// were added. It might be p8?
int i;
int &&f();
using Int = int;
using IntLRef = int&;
using IntRRef = int&&;
using InitListInt = std::initializer_list<int>;
using IntPtr = int*;
auto x3a = i;
decltype(auto) x3d = i;
using Int = decltype(x3a);
using Int = decltype(x3d);
auto x4a = (i);
decltype(auto) x4d = (i);
using Int = decltype(x4a);
using IntLRef = decltype(x4d);
auto x5a = f();
decltype(auto) x5d = f();
using Int = decltype(x5a);
using IntRRef = decltype(x5d);
auto x6a = { 1, 2 };
decltype(auto) x6d = { 1, 2 }; // expected-error {{cannot deduce 'decltype(auto)' from initializer list}}
using InitListInt = decltype(x6a);
auto *x7a = &i;
decltype(auto) *x7d = &i; // expected-error {{cannot form pointer to 'decltype(auto)'}}
using IntPtr = decltype(x7a);
struct S {};
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}}
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}}
decltype(auto) &f9(); // expected-error {{cannot form reference to 'decltype(auto)'}}
decltype(auto) (&f10())[10]; // expected-error {{cannot form array of 'decltype(auto)'}}
decltype(auto) ((((((v1)))))) = 0; // ok
decltype(auto) v2[1] = { 0 }; // expected-error {{cannot form array of 'decltype(auto)'}}
decltype(auto) &v3 = { 0 }; // expected-error {{cannot form reference to 'decltype(auto)'}}
decltype(auto) *v4 = { 0 }; // expected-error {{cannot form pointer to 'decltype(auto)'}}

View File

@ -0,0 +1,24 @@
// RUN: %clang_cc1 -pedantic -std=c++1y -emit-pch %s -o %t
// RUN: %clang_cc1 -pedantic -std=c++1y -include-pch %t -verify %s
#ifndef HEADER_INCLUDED
#define HEADER_INCLUDED
template<typename T> void f(T t) {
auto a = t.x;
decltype(auto) b = t.x;
auto c = (t.x);
decltype(auto) d = (t.x);
}
#else
struct Z {
int x : 5; // expected-note {{bit-field}}
};
// expected-error@12 {{non-const reference cannot bind to bit-field 'x'}}
template void f(Z); // expected-note {{in instantiation of}}
#endif

View File

@ -424,9 +424,13 @@ ISO/IEC JTC1/SC22/WG21 post-Bristol mailing ships.</p>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3472.pdf">N3472</a></td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3472.pdf">N3472</a></td>
<td class="full" align="center">Yes</td> <td class="full" align="center">Yes</td>
</tr> </tr>
<tr>
<td>decltype(auto)</td>
<td rowspan=2><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3638.html">N3638</a></td>
<td class="svn" align="center">SVN</td>
</tr>
<tr> <tr>
<td>Return type deduction for normal functions</td> <td>Return type deduction for normal functions</td>
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3638.html">N3638</a></td>
<td class="none" align="center">No</td> <td class="none" align="center">No</td>
</tr> </tr>
<tr> <tr>