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;
/// \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.
QualType getAutoDeductType() const;

View File

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

View File

@ -277,6 +277,11 @@ def warn_auto_storage_class : Warning<
def ext_auto_storage_class : ExtWarn<
"'auto' storage class specifier is not permitted in C++11, and will not "
"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<
"range-based for loop is a C++11 extension">, InGroup<CXX11>;
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">,
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
def override_keyword_only_allowed_on_virtual_member_functions : Error<
"only virtual member functions can be marked '%0'">;

View File

@ -38,8 +38,8 @@ namespace clang {
TST_void,
TST_char,
TST_wchar, // C++ wchar_t
TST_char16, // C++0x char16_t
TST_char32, // C++0x char32_t
TST_char16, // C++11 char16_t
TST_char32, // C++11 char32_t
TST_int,
TST_int128,
TST_half, // OpenCL half, ARM NEON __fp16
@ -57,11 +57,12 @@ namespace clang {
TST_typename, // Typedef, C++ class-name or enum name, etc.
TST_typeofType,
TST_typeofExpr,
TST_decltype, // C++0x decltype
TST_underlyingType, // __underlying_type for C++0x
TST_auto, // C++0x auto
TST_unknown_anytype, // __unknown_anytype extension
TST_atomic, // C11 _Atomic
TST_decltype, // C++11 decltype
TST_underlyingType, // __underlying_type for C++11
TST_auto, // C++11 auto
TST_decltype_auto, // C++1y decltype(auto)
TST_unknown_anytype, // __unknown_anytype extension
TST_atomic, // C11 _Atomic
TST_image1d_t, // OpenCL image1d_t
TST_image1d_array_t, // OpenCL image1d_array_t
TST_image1d_buffer_t, // OpenCL image1d_buffer_t
@ -145,7 +146,7 @@ namespace clang {
TSK_ExplicitSpecialization,
/// This template specialization was instantiated from a template
/// due to an explicit instantiation declaration request
/// (C++0x [temp.explicit]).
/// (C++11 [temp.explicit]).
TSK_ExplicitInstantiationDeclaration,
/// This template specialization was instantiated from a template
/// 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
/// token.
static ExprResult getExprAnnotation(Token &Tok) {
if (Tok.getAnnotationValue())
return ExprResult((Expr *)Tok.getAnnotationValue());
return ExprResult(true);
return ExprResult::getFromOpaquePointer(Tok.getAnnotationValue());
}
/// \brief Set the primary expression corresponding to the given annotation
/// token.
static void setExprAnnotation(Token &Tok, ExprResult ER) {
if (ER.isInvalid())
Tok.setAnnotationValue(0);
else
Tok.setAnnotationValue(ER.get());
Tok.setAnnotationValue(ER.getAsOpaquePointer());
}
public:

View File

@ -285,6 +285,7 @@ public:
static const TST TST_typeofType = clang::TST_typeofType;
static const TST TST_typeofExpr = clang::TST_typeofExpr;
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_auto = clang::TST_auto;
static const TST TST_unknown_anytype = clang::TST_unknown_anytype;
@ -504,6 +505,10 @@ public:
SourceRange getTypeofParensRange() const { return TypeofParensRange; }
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".
static const char *getSpecifierName(DeclSpec::TST T);
static const char *getSpecifierName(DeclSpec::TQ Q);

View File

@ -207,6 +207,15 @@ namespace clang {
assert((PtrWithInvalid & 0x01) == 0 && "Badly aligned pointer");
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

View File

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

View File

@ -1680,7 +1680,7 @@ QualType ASTNodeImporter::VisitUnaryTransformType(const UnaryTransformType *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 ToDeduced;
if (!FromDeduced.isNull()) {
@ -1689,7 +1689,7 @@ QualType ASTNodeImporter::VisitAutoType(const AutoType *T) {
return QualType();
}
return Importer.getToContext().getAutoType(ToDeduced);
return Importer.getToContext().getAutoType(ToDeduced, T->isDecltypeAuto());
}
QualType ASTNodeImporter::VisitRecordType(const RecordType *T) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -691,8 +691,14 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
Res = getExprAnnotation(Tok);
ConsumeToken();
break;
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
// unqualified-id: identifier
// constant: enumeration-constant

View File

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

View File

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

View File

@ -294,6 +294,11 @@ bool Declarator::isDeclarationOfFunction() const {
case TST_event_t:
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_typeofExpr:
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_auto: return "auto";
case DeclSpec::TST_decltype: return "(decltype)";
case DeclSpec::TST_decltype_auto: return "decltype(auto)";
case DeclSpec::TST_underlyingType: return "__underlying_type";
case DeclSpec::TST_unknown_anytype: return "__unknown_anytype";
case DeclSpec::TST_atomic: return "_Atomic";
@ -494,7 +500,7 @@ bool DeclSpec::SetStorageClassSpec(Sema &S, SCS SC, SourceLocation Loc,
}
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;
if (TypeSpecType == TST_unspecified && S.getLangOpts().CPlusPlus) {
if (SC == SCS_auto)
@ -834,6 +840,39 @@ void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP) {
// 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.
if (TypeAltiVecVector) {
if (TypeAltiVecBool) {
@ -973,7 +1012,7 @@ void DeclSpec::Finish(DiagnosticsEngine &D, Preprocessor &PP) {
StorageClassSpecLoc = SourceLocation();
}
// 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)
Diag(D, TSTLoc, diag::ext_auto_type_specifier);
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
// Decl so we can diagnose uses of it in its own initializer.
if (D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto &&
R->getContainedAutoType())
if (D.getDeclSpec().containsPlaceholderType() && R->getContainedAutoType())
ParsingInitForAutoVars.insert(NewVD);
if (D.isInvalidType() || Invalid)
@ -8166,7 +8165,7 @@ Sema::FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS,
getASTContext().addUnnamedTag(Tag);
return BuildDeclaratorGroup(Decls.data(), Decls.size(),
DS.getTypeSpecType() == DeclSpec::TST_auto);
DS.containsPlaceholderType());
}
/// 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 PlacementRParen, SourceRange TypeIdParens,
Declarator &D, Expr *Initializer) {
bool TypeContainsAuto = D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto;
bool TypeContainsAuto = D.getDeclSpec().containsPlaceholderType();
Expr *ArraySize = 0;
// 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.
if (isa<TemplateTypeParmType>(Replacement)) {
QualType Result = Replacement;
TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result);
TemplateTypeParmTypeLoc NewTL =
TLB.push<TemplateTypeParmTypeLoc>(Result);
NewTL.setNameLoc(TL.getNameLoc());
return Result;
} else {
QualType Result = RebuildAutoType(Replacement);
QualType Result = RebuildAutoType(Replacement,
TL.getTypePtr()->isDecltypeAuto());
AutoTypeLoc NewTL = TLB.push<AutoTypeLoc>(Result);
NewTL.setNameLoc(TL.getNameLoc());
return Result;
@ -3657,6 +3659,24 @@ Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *&Init,
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();
LocalInstantiationScope InstScope(*this);

View File

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

View File

@ -994,11 +994,14 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
}
break;
case DeclSpec::TST_auto: {
case DeclSpec::TST_auto:
// 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;
}
case DeclSpec::TST_unknown_anytype:
Result = Context.UnknownAnyTy;
@ -1457,12 +1460,6 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM,
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 the element type is a struct or union that contains a variadic
// 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
// type (this is checked later) and we can skip this. In other languages
// using auto, we need to check regardless.
if (D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto &&
if (D.getDeclSpec().containsPlaceholderType() &&
(!SemaRef.getLangOpts().CPlusPlus11 || !D.isFunctionDeclarator())) {
int Error = -1;
@ -2402,6 +2399,46 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
(T->castAs<FunctionProtoType>()->getTypeQuals() != 0 ||
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.
// DeclTypeInfos are ordered from the identifier out, which is
// 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,
SourceRange(DeclType.Loc, DeclType.EndLoc), Name);
break;
@ -2560,7 +2606,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
<< T << D.getDeclSpec().getSourceRange();
D.setInvalidType(true);
} 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(),
diag::err_trailing_return_without_auto)
<< T << D.getDeclSpec().getSourceRange();

View File

@ -754,17 +754,17 @@ public:
UnaryTransformType::UTTKind UKind,
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.
/// Subclasses may override this routine to provide different behavior.
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.
QualType RebuildAutoType(QualType Deduced) {
return SemaRef.Context.getAutoType(Deduced);
QualType RebuildAutoType(QualType Deduced, bool IsDecltypeAuto) {
return SemaRef.Context.getAutoType(Deduced, IsDecltypeAuto);
}
/// \brief Build a new template specialization type.
@ -3407,7 +3407,7 @@ TreeTransform<Derived>::TransformQualifiedType(TypeLocBuilder &TLB,
Qs.removeObjCLifetime();
Deduced = SemaRef.Context.getQualifiedType(Deduced.getUnqualifiedType(),
Qs);
Result = SemaRef.Context.getAutoType(Deduced);
Result = SemaRef.Context.getAutoType(Deduced, AutoTy->isDecltypeAuto());
TLB.TypeWasModifiedSafely(Result);
} else {
// Otherwise, complain about the addition of a qualifier to an
@ -4501,7 +4501,7 @@ QualType TreeTransform<Derived>::TransformAutoType(TypeLocBuilder &TLB,
QualType Result = TL.getType();
if (getDerived().AlwaysRebuild() || NewDeduced != OldDeduced) {
Result = getDerived().RebuildAutoType(NewDeduced);
Result = getDerived().RebuildAutoType(NewDeduced, T->isDecltypeAuto());
if (Result.isNull())
return QualType();
}

View File

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

View File

@ -245,6 +245,7 @@ void ASTTypeWriter::VisitUnaryTransformType(const UnaryTransformType *T) {
void ASTTypeWriter::VisitAutoType(const AutoType *T) {
Writer.AddTypeRef(T->getDeducedType(), Record);
Record.push_back(T->isDecltypeAuto());
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 class="full" align="center">Yes</td>
</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>
<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>
</tr>
<tr>